config.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. // Copyright © 2022 Ettore Di Giacinto <[email protected]>
  2. //
  3. // This program is free software; you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation; either version 2 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License along
  14. // with this program; if not, see <http://www.gnu.org/licenses/>.
  15. package config
  16. import (
  17. "fmt"
  18. "math/bits"
  19. "os"
  20. "time"
  21. "github.com/ipfs/go-log"
  22. "github.com/libp2p/go-libp2p"
  23. connmanager "github.com/libp2p/go-libp2p-connmgr"
  24. "github.com/libp2p/go-libp2p-core/network"
  25. "github.com/libp2p/go-libp2p-core/peer"
  26. dht "github.com/libp2p/go-libp2p-kad-dht"
  27. mplex "github.com/libp2p/go-libp2p-mplex"
  28. rcmgr "github.com/libp2p/go-libp2p-resource-manager"
  29. yamux "github.com/libp2p/go-libp2p-yamux"
  30. "github.com/libp2p/go-libp2p/p2p/host/autorelay"
  31. "github.com/mudler/edgevpn/pkg/blockchain"
  32. "github.com/mudler/edgevpn/pkg/crypto"
  33. "github.com/mudler/edgevpn/pkg/discovery"
  34. "github.com/mudler/edgevpn/pkg/logger"
  35. "github.com/mudler/edgevpn/pkg/node"
  36. "github.com/mudler/edgevpn/pkg/services"
  37. "github.com/mudler/edgevpn/pkg/vpn"
  38. "github.com/peterbourgon/diskv"
  39. "github.com/songgao/water"
  40. )
  41. // Config is the config struct for the node and the default EdgeVPN services
  42. // It is used to generate opts for the node and the services before start.
  43. type Config struct {
  44. NetworkConfig, NetworkToken string
  45. Address string
  46. Router string
  47. Interface string
  48. Libp2pLogLevel, LogLevel string
  49. LowProfile, VPNLowProfile, BootstrapIface bool
  50. Blacklist []string
  51. Concurrency int
  52. FrameTimeout string
  53. ChannelBufferSize, InterfaceMTU, PacketMTU int
  54. NAT NAT
  55. Connection Connection
  56. Discovery Discovery
  57. Ledger Ledger
  58. Limit ResourceLimit
  59. }
  60. type ResourceLimit struct {
  61. FileLimit string
  62. LimitConfig *node.NetLimitConfig
  63. Scope string
  64. MaxConns int
  65. StaticMin int64
  66. StaticMax int64
  67. Enable bool
  68. }
  69. // Ledger is the ledger configuration structure
  70. type Ledger struct {
  71. AnnounceInterval, SyncInterval time.Duration
  72. StateDir string
  73. }
  74. // Discovery allows to enable/disable discovery and
  75. // set bootstrap peers
  76. type Discovery struct {
  77. DHT, MDNS bool
  78. BootstrapPeers []string
  79. Interval time.Duration
  80. }
  81. // Connection is the configuration section
  82. // relative to the connection services
  83. type Connection struct {
  84. HolePunch bool
  85. AutoRelay bool
  86. AutoRelayDiscoveryInterval time.Duration
  87. RelayV1 bool
  88. StaticRelays []string
  89. Mplex bool
  90. MaxConnections int
  91. MaxStreams int
  92. }
  93. // NAT is the structure relative to NAT configuration settings
  94. // It allows to enable/disable the service and NAT mapping, and rate limiting too.
  95. type NAT struct {
  96. Service bool
  97. Map bool
  98. RateLimit bool
  99. RateLimitGlobal, RateLimitPeer int
  100. RateLimitInterval time.Duration
  101. }
  102. // Validate returns error if the configuration is not valid
  103. func (c Config) Validate() error {
  104. if c.NetworkConfig == "" &&
  105. c.NetworkToken == "" {
  106. return fmt.Errorf("EDGEVPNCONFIG or EDGEVPNTOKEN not supplied. At least a config file is required")
  107. }
  108. return nil
  109. }
  110. func peers2List(peers []string) discovery.AddrList {
  111. addrsList := discovery.AddrList{}
  112. for _, p := range peers {
  113. addrsList.Set(p)
  114. }
  115. return addrsList
  116. }
  117. func peers2AddrInfo(peers []string) []peer.AddrInfo {
  118. addrsList := []peer.AddrInfo{}
  119. for _, p := range peers {
  120. pi, err := peer.AddrInfoFromString(p)
  121. if err == nil {
  122. addrsList = append(addrsList, *pi)
  123. }
  124. }
  125. return addrsList
  126. }
  127. // ToOpts returns node and vpn options from a configuration
  128. func (c Config) ToOpts(l *logger.Logger) ([]node.Option, []vpn.Option, error) {
  129. if err := c.Validate(); err != nil {
  130. return nil, nil, err
  131. }
  132. config := c.NetworkConfig
  133. address := c.Address
  134. router := c.Router
  135. iface := c.Interface
  136. logLevel := c.LogLevel
  137. libp2plogLevel := c.Libp2pLogLevel
  138. dhtE, mDNS := c.Discovery.DHT, c.Discovery.MDNS
  139. ledgerState := c.Ledger.StateDir
  140. peers := c.Discovery.BootstrapPeers
  141. lvl, err := log.LevelFromString(logLevel)
  142. if err != nil {
  143. lvl = log.LevelError
  144. }
  145. llger := logger.New(lvl)
  146. libp2plvl, err := log.LevelFromString(libp2plogLevel)
  147. if err != nil {
  148. libp2plvl = log.LevelFatal
  149. }
  150. token := c.NetworkToken
  151. addrsList := peers2List(peers)
  152. dhtOpts := []dht.Option{}
  153. if c.LowProfile {
  154. dhtOpts = append(dhtOpts, dht.BucketSize(20))
  155. }
  156. opts := []node.Option{
  157. node.WithDiscoveryInterval(c.Discovery.Interval),
  158. node.WithLedgerAnnounceTime(c.Ledger.AnnounceInterval),
  159. node.WithLedgerInterval(c.Ledger.SyncInterval),
  160. node.Logger(llger),
  161. node.WithDiscoveryBootstrapPeers(addrsList),
  162. node.WithBlacklist(c.Blacklist...),
  163. node.LibP2PLogLevel(libp2plvl),
  164. node.WithInterfaceAddress(address),
  165. node.WithSealer(&crypto.AESSealer{}),
  166. node.FromBase64(mDNS, dhtE, token, dhtOpts...),
  167. node.FromYaml(mDNS, dhtE, config, dhtOpts...),
  168. }
  169. vpnOpts := []vpn.Option{
  170. vpn.WithConcurrency(c.Concurrency),
  171. vpn.WithInterfaceAddress(address),
  172. vpn.WithLedgerAnnounceTime(c.Ledger.AnnounceInterval),
  173. vpn.Logger(llger),
  174. vpn.WithTimeout(c.FrameTimeout),
  175. vpn.WithInterfaceType(water.TUN),
  176. vpn.NetLinkBootstrap(c.BootstrapIface),
  177. vpn.WithChannelBufferSize(c.ChannelBufferSize),
  178. vpn.WithInterfaceMTU(c.InterfaceMTU),
  179. vpn.WithPacketMTU(c.PacketMTU),
  180. vpn.WithRouterAddress(router),
  181. vpn.WithInterfaceName(iface),
  182. vpn.WithMaxStreams(c.Connection.MaxStreams),
  183. }
  184. if c.VPNLowProfile {
  185. vpnOpts = append(vpnOpts, vpn.LowProfile)
  186. }
  187. libp2pOpts := []libp2p.Option{libp2p.UserAgent("edgevpn")}
  188. // AutoRelay section configuration
  189. if c.Connection.AutoRelay {
  190. relayOpts := []autorelay.Option{}
  191. if c.Connection.RelayV1 {
  192. relayOpts = append(relayOpts, autorelay.WithCircuitV1Support())
  193. }
  194. // If no relays are specified and no discovery interval, then just use default static relays (to be deprecated)
  195. if len(c.Connection.StaticRelays) == 0 && c.Connection.AutoRelayDiscoveryInterval == 0 {
  196. relayOpts = append(relayOpts, autorelay.WithDefaultStaticRelays())
  197. } else if len(c.Connection.StaticRelays) > 0 {
  198. relayOpts = append(relayOpts, autorelay.WithStaticRelays(peers2AddrInfo(c.Connection.StaticRelays)))
  199. } else {
  200. peerChan := make(chan peer.AddrInfo)
  201. // Add AutoRelayFeederService (needs a DHT Service discovery)
  202. opts = append(opts, func(cfg *node.Config) error {
  203. for _, sd := range cfg.ServiceDiscovery {
  204. switch d := sd.(type) {
  205. case *discovery.DHT:
  206. llger.Debugf("DHT automatic relay discovery configured every '%s'\n", c.Connection.AutoRelayDiscoveryInterval.String())
  207. cfg.NetworkServices = append(cfg.NetworkServices, services.AutoRelayFeederService(llger, peerChan, d, c.Connection.AutoRelayDiscoveryInterval))
  208. }
  209. }
  210. return nil
  211. })
  212. relayOpts = append(relayOpts, autorelay.WithPeerSource(peerChan))
  213. }
  214. libp2pOpts = append(libp2pOpts,
  215. libp2p.EnableAutoRelay(relayOpts...))
  216. }
  217. if c.Connection.Mplex {
  218. libp2pOpts = append(libp2pOpts,
  219. libp2p.ChainOptions(
  220. libp2p.Muxer("/yamux/1.0.0", yamux.DefaultTransport),
  221. libp2p.Muxer("/mplex/6.7.0", mplex.DefaultTransport),
  222. ))
  223. }
  224. if c.NAT.RateLimit {
  225. libp2pOpts = append(libp2pOpts, libp2p.AutoNATServiceRateLimit(
  226. c.NAT.RateLimitGlobal,
  227. c.NAT.RateLimitPeer,
  228. c.NAT.RateLimitInterval,
  229. ))
  230. }
  231. if c.Connection.MaxConnections != 0 {
  232. cm, err := connmanager.NewConnManager(
  233. 1,
  234. c.Connection.MaxConnections,
  235. connmanager.WithGracePeriod(80*time.Second),
  236. )
  237. if err != nil {
  238. llger.Fatal("could not create connection manager")
  239. }
  240. libp2pOpts = append(libp2pOpts, libp2p.ConnectionManager(cm))
  241. }
  242. if !c.Limit.Enable {
  243. libp2pOpts = append(libp2pOpts, libp2p.ResourceManager(network.NullResourceManager))
  244. } else {
  245. var limiter *rcmgr.BasicLimiter
  246. if c.Limit.FileLimit != "" {
  247. limitFile, err := os.Open(c.Limit.FileLimit)
  248. if err != nil {
  249. return opts, vpnOpts, err
  250. }
  251. defer limitFile.Close()
  252. limiter, err = rcmgr.NewDefaultLimiterFromJSON(limitFile)
  253. if err != nil {
  254. return opts, vpnOpts, err
  255. }
  256. } else if c.Limit.MaxConns != 0 {
  257. min := int64(1 << 30)
  258. max := int64(4 << 30)
  259. if c.Limit.StaticMin != 0 {
  260. min = c.Limit.StaticMin
  261. }
  262. if c.Limit.StaticMax != 0 {
  263. max = c.Limit.StaticMax
  264. }
  265. defaultLimits := rcmgr.DefaultLimits.WithSystemMemory(.125, min, max)
  266. maxconns := int(c.Limit.MaxConns)
  267. if 2*maxconns > defaultLimits.SystemBaseLimit.ConnsInbound {
  268. // adjust conns to 2x to allow for two conns per peer (TCP+QUIC)
  269. defaultLimits.SystemBaseLimit.ConnsInbound = logScale(2 * maxconns)
  270. defaultLimits.SystemBaseLimit.ConnsOutbound = logScale(2 * maxconns)
  271. defaultLimits.SystemBaseLimit.Conns = logScale(4 * maxconns)
  272. defaultLimits.SystemBaseLimit.StreamsInbound = logScale(16 * maxconns)
  273. defaultLimits.SystemBaseLimit.StreamsOutbound = logScale(64 * maxconns)
  274. defaultLimits.SystemBaseLimit.Streams = logScale(64 * maxconns)
  275. if 2*maxconns > defaultLimits.SystemBaseLimit.FD {
  276. defaultLimits.SystemBaseLimit.FD = logScale(2 * maxconns)
  277. }
  278. defaultLimits.ServiceBaseLimit.StreamsInbound = logScale(8 * maxconns)
  279. defaultLimits.ServiceBaseLimit.StreamsOutbound = logScale(32 * maxconns)
  280. defaultLimits.ServiceBaseLimit.Streams = logScale(32 * maxconns)
  281. defaultLimits.ProtocolBaseLimit.StreamsInbound = logScale(8 * maxconns)
  282. defaultLimits.ProtocolBaseLimit.StreamsOutbound = logScale(32 * maxconns)
  283. defaultLimits.ProtocolBaseLimit.Streams = logScale(32 * maxconns)
  284. }
  285. limiter = rcmgr.NewStaticLimiter(defaultLimits)
  286. } else {
  287. limiter = rcmgr.NewDefaultLimiter()
  288. }
  289. libp2p.SetDefaultServiceLimits(limiter)
  290. rc, err := rcmgr.NewResourceManager(limiter)
  291. if err != nil {
  292. llger.Fatal("could not create resource manager")
  293. }
  294. if c.Limit.LimitConfig != nil {
  295. if err := node.NetSetLimit(rc, c.Limit.Scope, *c.Limit.LimitConfig); err != nil {
  296. return opts, vpnOpts, err
  297. }
  298. }
  299. libp2pOpts = append(libp2pOpts, libp2p.ResourceManager(rc))
  300. }
  301. if c.Connection.HolePunch {
  302. libp2pOpts = append(libp2pOpts, libp2p.EnableHolePunching())
  303. }
  304. if c.NAT.Service {
  305. libp2pOpts = append(libp2pOpts, libp2p.EnableNATService())
  306. }
  307. if c.NAT.Map {
  308. libp2pOpts = append(libp2pOpts, libp2p.NATPortMap())
  309. }
  310. opts = append(opts, node.WithLibp2pOptions(libp2pOpts...))
  311. if ledgerState != "" {
  312. opts = append(opts, node.WithStore(blockchain.NewDiskStore(diskv.New(diskv.Options{
  313. BasePath: ledgerState,
  314. CacheSizeMax: uint64(50), // 50MB
  315. }))))
  316. } else {
  317. opts = append(opts, node.WithStore(&blockchain.MemoryStore{}))
  318. }
  319. return opts, vpnOpts, nil
  320. }
  321. func logScale(val int) int {
  322. bitlen := bits.Len(uint(val))
  323. return 1 << bitlen
  324. }