Browse Source

Allow to configure rcmgr #8

Ettore Di Giacinto 3 years ago
parent
commit
fad3dfed7a
2 changed files with 164 additions and 0 deletions
  1. 37 0
      pkg/config/config.go
  2. 127 0
      pkg/node/resources.go

+ 37 - 0
pkg/config/config.go

@@ -17,12 +17,14 @@ package config
 
 import (
 	"fmt"
+	"os"
 	"time"
 
 	"github.com/ipfs/go-log"
 	"github.com/libp2p/go-libp2p"
 	connmanager "github.com/libp2p/go-libp2p-connmgr"
 	dht "github.com/libp2p/go-libp2p-kad-dht"
+	rcmgr "github.com/libp2p/go-libp2p-resource-manager"
 	"github.com/mudler/edgevpn/pkg/blockchain"
 	"github.com/mudler/edgevpn/pkg/crypto"
 	"github.com/mudler/edgevpn/pkg/discovery"
@@ -51,6 +53,13 @@ type Config struct {
 	Connection                                 Connection
 	Discovery                                  Discovery
 	Ledger                                     Ledger
+	Limit                                      ResourceLimit
+}
+
+type ResourceLimit struct {
+	FileLimit   string
+	LimitConfig *node.NetLimitConfig
+	Scope       string
 }
 
 // Ledger is the ledger configuration structure
@@ -201,6 +210,34 @@ func (c Config) ToOpts(l *logger.Logger) ([]node.Option, []vpn.Option, error) {
 
 	libp2pOpts = append(libp2pOpts, libp2p.ConnectionManager(cm))
 
+	var limiter *rcmgr.BasicLimiter
+
+	if c.Limit.FileLimit != "" {
+		limitFile, err := os.Open(c.Limit.FileLimit)
+		if err != nil {
+			return opts, vpnOpts, err
+		}
+		defer limitFile.Close()
+
+		limiter, err = rcmgr.NewDefaultLimiterFromJSON(limitFile)
+		if err != nil {
+			return opts, vpnOpts, err
+		}
+	} else {
+		limiter = rcmgr.NewDefaultLimiter()
+	}
+
+	libp2p.SetDefaultServiceLimits(limiter)
+
+	rc, err := rcmgr.NewResourceManager(limiter)
+	if c.Limit.LimitConfig != nil {
+		if err := node.NetSetLimit(rc, c.Limit.Scope, *c.Limit.LimitConfig); err != nil {
+			return opts, vpnOpts, err
+		}
+	}
+
+	libp2pOpts = append(libp2pOpts, libp2p.ResourceManager(rc))
+
 	if c.Connection.HolePunch {
 		libp2pOpts = append(libp2pOpts, libp2p.EnableHolePunching())
 	}

+ 127 - 0
pkg/node/resources.go

@@ -0,0 +1,127 @@
+// Copyright © 2022 Ettore Di Giacinto <[email protected]>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, see <http://www.gnu.org/licenses/>.
+
+package node
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/libp2p/go-libp2p-core/network"
+	"github.com/libp2p/go-libp2p-core/peer"
+	libp2pprotocol "github.com/libp2p/go-libp2p-core/protocol"
+	rcmgr "github.com/libp2p/go-libp2p-resource-manager"
+)
+
+type NetLimitConfig struct {
+	Dynamic bool `json:",omitempty"`
+	// set if Dynamic is false
+	Memory int64 `json:",omitempty"`
+	// set if Dynamic is true
+	MemoryFraction float64 `json:",omitempty"`
+	MinMemory      int64   `json:",omitempty"`
+	MaxMemory      int64   `json:",omitempty"`
+
+	Streams, StreamsInbound, StreamsOutbound int
+	Conns, ConnsInbound, ConnsOutbound       int
+	FD                                       int
+}
+
+func NetSetLimit(mgr network.ResourceManager, scope string, limit NetLimitConfig) error {
+	setLimit := func(s network.ResourceScope) error {
+		limiter, ok := s.(rcmgr.ResourceScopeLimiter)
+		if !ok {
+			return fmt.Errorf("resource scope doesn't implement ResourceScopeLimiter interface")
+		}
+
+		var newLimit rcmgr.Limit
+		if limit.Dynamic {
+			newLimit = &rcmgr.DynamicLimit{
+				MemoryLimit: rcmgr.MemoryLimit{
+					MemoryFraction: limit.MemoryFraction,
+					MinMemory:      limit.MinMemory,
+					MaxMemory:      limit.MaxMemory,
+				},
+				BaseLimit: rcmgr.BaseLimit{
+					Streams:         limit.Streams,
+					StreamsInbound:  limit.StreamsInbound,
+					StreamsOutbound: limit.StreamsOutbound,
+					Conns:           limit.Conns,
+					ConnsInbound:    limit.ConnsInbound,
+					ConnsOutbound:   limit.ConnsOutbound,
+					FD:              limit.FD,
+				},
+			}
+		} else {
+			newLimit = &rcmgr.StaticLimit{
+				Memory: limit.Memory,
+				BaseLimit: rcmgr.BaseLimit{
+					Streams:         limit.Streams,
+					StreamsInbound:  limit.StreamsInbound,
+					StreamsOutbound: limit.StreamsOutbound,
+					Conns:           limit.Conns,
+					ConnsInbound:    limit.ConnsInbound,
+					ConnsOutbound:   limit.ConnsOutbound,
+					FD:              limit.FD,
+				},
+			}
+		}
+
+		limiter.SetLimit(newLimit)
+		return nil
+	}
+
+	switch {
+	case scope == "system":
+		err := mgr.ViewSystem(func(s network.ResourceScope) error {
+			return setLimit(s)
+		})
+		return err
+
+	case scope == "transient":
+		err := mgr.ViewTransient(func(s network.ResourceScope) error {
+			return setLimit(s)
+		})
+		return err
+
+	case strings.HasPrefix(scope, "svc:"):
+		svc := scope[4:]
+		err := mgr.ViewService(svc, func(s network.ServiceScope) error {
+			return setLimit(s)
+		})
+		return err
+
+	case strings.HasPrefix(scope, "proto:"):
+		proto := scope[6:]
+		err := mgr.ViewProtocol(libp2pprotocol.ID(proto), func(s network.ProtocolScope) error {
+			return setLimit(s)
+		})
+		return err
+
+	case strings.HasPrefix(scope, "peer:"):
+		p := scope[5:]
+		pid, err := peer.Decode(p)
+		if err != nil {
+			return fmt.Errorf("invalid peer ID: %s: %w", p, err)
+		}
+		err = mgr.ViewPeer(pid, func(s network.PeerScope) error {
+			return setLimit(s)
+		})
+		return err
+
+	default:
+		return fmt.Errorf("invalid scope %s", scope)
+	}
+}