mirror of
https://github.com/wlynxg/NetHive.git
synced 2025-09-27 03:15:59 +08:00
254 lines
5.3 KiB
Go
254 lines
5.3 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"net/netip"
|
|
|
|
pool "github.com/libp2p/go-buffer-pool"
|
|
"github.com/libp2p/go-msgio"
|
|
"github.com/wlynxg/NetHive/core/route"
|
|
"github.com/wlynxg/NetHive/pkgs/xpool"
|
|
|
|
"github.com/wlynxg/NetHive/core/config"
|
|
"github.com/wlynxg/NetHive/core/device"
|
|
mlog "github.com/wlynxg/NetHive/pkgs/log"
|
|
"github.com/wlynxg/NetHive/pkgs/xsync"
|
|
|
|
"github.com/libp2p/go-libp2p"
|
|
dht "github.com/libp2p/go-libp2p-kad-dht"
|
|
"github.com/libp2p/go-libp2p/core/host"
|
|
"github.com/libp2p/go-libp2p/core/network"
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
"github.com/libp2p/go-libp2p/p2p/discovery/mdns"
|
|
"github.com/libp2p/go-libp2p/p2p/discovery/routing"
|
|
)
|
|
|
|
const (
|
|
BuffSize = 1500
|
|
ChanSize = 15000
|
|
VPNStreamProtocol = "/NetHive/vpn"
|
|
)
|
|
|
|
type PacketChan chan *Payload
|
|
|
|
type Engine struct {
|
|
log *mlog.Logger
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
cfg *config.Config
|
|
// tun device
|
|
device device.Device
|
|
|
|
host host.Host
|
|
dht *dht.IpfsDHT
|
|
discovery *routing.RoutingDiscovery
|
|
mdns mdns.Service
|
|
|
|
relayChan chan peer.AddrInfo
|
|
|
|
devWriter PacketChan
|
|
devReader PacketChan
|
|
errChan chan error
|
|
|
|
bufferPool *pool.BufferPool
|
|
payloadPool xpool.Pool[*Payload]
|
|
|
|
routeTable struct {
|
|
m xsync.Map[string, netip.Prefix]
|
|
id xsync.Map[string, PacketChan]
|
|
addr xsync.Map[netip.Addr, PacketChan]
|
|
}
|
|
}
|
|
|
|
func Run(ctx context.Context, cfg *config.Config) (*Engine, error) {
|
|
var (
|
|
e = new(Engine)
|
|
err error
|
|
options []libp2p.Option
|
|
)
|
|
|
|
e.cfg = cfg
|
|
mlog.SetOutputTypes(cfg.LogConfigs...)
|
|
e.log = mlog.New("engine")
|
|
e.ctx, e.cancel = context.WithCancel(ctx)
|
|
e.devWriter = make(PacketChan, ChanSize)
|
|
e.devReader = make(PacketChan, ChanSize)
|
|
|
|
e.bufferPool = &pool.BufferPool{}
|
|
e.payloadPool = xpool.New[*Payload](func() *Payload {
|
|
return &Payload{}
|
|
})
|
|
|
|
pk, err := cfg.PrivateKey.PrivKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, libp2p.Identity(pk))
|
|
|
|
if len(cfg.Relays) > 0 {
|
|
var relays []peer.AddrInfo
|
|
for _, relay := range cfg.Relays {
|
|
addrInfo, err := peer.AddrInfoFromString(relay)
|
|
if err != nil {
|
|
e.log.Warnf("fail to parse '%s': %v", relay, err)
|
|
continue
|
|
}
|
|
relays = append(relays, *addrInfo)
|
|
}
|
|
options = append(options, libp2p.EnableAutoRelayWithStaticRelays(relays))
|
|
} else if cfg.EnableAutoRelay {
|
|
e.relayChan = make(chan peer.AddrInfo, ChanSize)
|
|
options = append(options, libp2p.EnableAutoRelayWithPeerSource(func(ctx context.Context, num int) <-chan peer.AddrInfo {
|
|
c := make(chan peer.AddrInfo, num)
|
|
go func() {
|
|
defer close(c)
|
|
for ; num >= 0; num-- {
|
|
select {
|
|
case v, ok := <-e.relayChan:
|
|
if !ok {
|
|
return
|
|
}
|
|
e.log.Debugf("auto relay find node: %v", v)
|
|
select {
|
|
case c <- v:
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
return c
|
|
}))
|
|
}
|
|
|
|
node, err := libp2p.New(options...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
e.host = node
|
|
e.log.Infof("host ID: %s", node.ID().String())
|
|
e.dht, err = dht.New(e.ctx, e.host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e.discovery = routing.NewRoutingDiscovery(e.dht)
|
|
|
|
return e, nil
|
|
}
|
|
|
|
func (e *Engine) Run() error {
|
|
var err error
|
|
defer e.cancel()
|
|
|
|
// TUN init
|
|
e.device, err = device.CreateTUN(e.cfg.TUNName, e.cfg.MTU)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
name, err := e.device.Name()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := e.device.AddAddress(e.cfg.LocalAddr); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := e.device.Up(); err != nil {
|
|
return err
|
|
}
|
|
|
|
for id, prefix := range e.cfg.PeersRouteTable {
|
|
e.routeTable.m.Store(id, prefix)
|
|
|
|
err := route.Add(name, prefix)
|
|
if err != nil {
|
|
e.log.Warnf("fail to add %s's route %s: %v", id, prefix, err)
|
|
continue
|
|
}
|
|
e.log.Debugf("successfully add %s's route: %s", id, prefix)
|
|
}
|
|
|
|
if len(e.cfg.Bootstraps) > 0 {
|
|
if err := e.EnableDHT(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if e.cfg.EnableMDNS {
|
|
if err := e.EnableMdns(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(e.cfg.Relays) == 0 && e.cfg.EnableAutoRelay {
|
|
// start auto relay detect
|
|
go e.autoRelayFinder(e.ctx)
|
|
}
|
|
|
|
e.host.SetStreamHandler(VPNStreamProtocol, e.VPNHandler)
|
|
|
|
go e.RoutineTUNReader()
|
|
go e.RoutineTUNWriter()
|
|
go e.RoutineRouteTableWriter()
|
|
|
|
e.log.Infof("listen addrs: %s", e.host.Addrs())
|
|
e.log.Infof("protocol handles: %s", e.host.Mux().Protocols())
|
|
|
|
if err := <-e.errChan; err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *Engine) VPNHandler(stream network.Stream) {
|
|
e.log.Debugf("[%s] connect by %s", stream.Conn().RemotePeer(), stream.Conn().RemoteMultiaddr())
|
|
|
|
id := stream.Conn().RemotePeer().String()
|
|
if _, ok := e.routeTable.m.Load(id); !ok {
|
|
stream.Close()
|
|
return
|
|
}
|
|
|
|
mr := msgio.NewVarintReaderSize(stream, network.MessageSizeMax)
|
|
mw := msgio.NewVarintWriter(stream)
|
|
|
|
peerChan, ok := e.routeTable.id.Load(id)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
for {
|
|
msg, err := mr.ReadMsg()
|
|
if err != nil {
|
|
e.log.Errorf("Peer [%s] read msg error: %s", id, err)
|
|
return
|
|
}
|
|
|
|
payload := e.payloadPool.Get()
|
|
payload.Data = e.bufferPool.Get(len(msg))
|
|
copy(payload.Data, msg)
|
|
mr.ReleaseMsg(msg)
|
|
e.devWriter <- payload
|
|
}
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case payload := <-peerChan:
|
|
err := mw.WriteMsg(payload.Data)
|
|
e.bufferPool.Put(payload.Data)
|
|
e.payloadPool.Put(payload)
|
|
if err != nil {
|
|
e.log.Errorf("Peer [%s] write msg error: %s", id, err)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
}
|