add entrypoint for router handler
This commit is contained in:
+113
-76
@@ -1,6 +1,7 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
@@ -97,7 +98,7 @@ func (h *routerHandler) handleAssociate(ctx context.Context, conn net.Conn, netw
|
||||
ID: connectorID.String(),
|
||||
Name: clientID,
|
||||
Node: h.id,
|
||||
Network: network,
|
||||
Network: "udp",
|
||||
Address: h.md.entryPoint,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -113,91 +114,127 @@ func (h *routerHandler) handleAssociate(ctx context.Context, conn net.Conn, netw
|
||||
|
||||
log.Debugf("%s/%s: router=%s, connector=%s, weight=%d established", host, network, routerID, connectorID, connectorID.Weight())
|
||||
|
||||
return h.routing(ctx, conn, routerID, log)
|
||||
}
|
||||
|
||||
func (h *routerHandler) routing(ctx context.Context, conn net.Conn, routerID relay.TunnelID, log logger.Logger) error {
|
||||
b := bufpool.Get(h.md.bufferSize)
|
||||
defer bufpool.Put(b)
|
||||
|
||||
rid := routerID.String()
|
||||
|
||||
for {
|
||||
err := func() error {
|
||||
n, err := conn.Read(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b := bufpool.Get(h.md.bufferSize)
|
||||
|
||||
var dstIP net.IP
|
||||
|
||||
if waterutil.IsIPv4(b[:n]) {
|
||||
header, err := ipv4.ParseHeader(b[:n])
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
dstIP = header.Dst
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
log.Tracef("%s >> %s %-4s %d/%-4d %-4x %d",
|
||||
header.Src, header.Dst, xip.Protocol(waterutil.IPv4Protocol(b[:n])),
|
||||
header.Len, header.TotalLen, header.ID, header.Flags)
|
||||
}
|
||||
} else if waterutil.IsIPv6(b[:n]) {
|
||||
header, err := ipv6.ParseHeader(b[:n])
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
dstIP = header.Dst
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
log.Tracef("%s >> %s %s %d %d",
|
||||
header.Src, header.Dst,
|
||||
xip.Protocol(waterutil.IPProtocol(header.NextHeader)),
|
||||
header.PayloadLen, header.TrafficClass)
|
||||
}
|
||||
} else {
|
||||
log.Warnf("unknown packet, discarded(%d)", n)
|
||||
return nil
|
||||
}
|
||||
|
||||
var route *router.Route
|
||||
if r := registry.RouterRegistry().Get(rid); r != nil {
|
||||
route = r.GetRoute(ctx, dstIP, router.IDOption(rid))
|
||||
}
|
||||
if route == nil && h.md.router != nil {
|
||||
route = h.md.router.GetRoute(ctx, dstIP, router.IDOption(rid))
|
||||
}
|
||||
if route == nil || route.Gateway == "" {
|
||||
// no route to host, discard
|
||||
return nil
|
||||
}
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
log.Tracef("route for %s: %s -> %s", dstIP, route.Dst, route.Gateway)
|
||||
}
|
||||
|
||||
if c := h.pool.Get(routerID, route.Gateway); c != nil {
|
||||
if w := c.Writer(); w != nil {
|
||||
w.Write(b[:n])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
n, err := conn.Read(b)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
h.handlePacket(ctx, b[:n], routerID, log)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *routerHandler) handlePacket(ctx context.Context, data []byte, routerID relay.TunnelID, log logger.Logger) error {
|
||||
defer bufpool.Put(data)
|
||||
|
||||
var dstIP net.IP
|
||||
if waterutil.IsIPv4(data) {
|
||||
header, err := ipv4.ParseHeader(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstIP = header.Dst
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
log.Tracef("%s >> %s %-4s %d/%-4d %-4x %d",
|
||||
header.Src, header.Dst, xip.Protocol(waterutil.IPv4Protocol(data)),
|
||||
header.Len, header.TotalLen, header.ID, header.Flags)
|
||||
}
|
||||
} else if waterutil.IsIPv6(data) {
|
||||
header, err := ipv6.ParseHeader(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstIP = header.Dst
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
log.Tracef("%s >> %s %s %d %d",
|
||||
header.Src, header.Dst,
|
||||
xip.Protocol(waterutil.IPProtocol(header.NextHeader)),
|
||||
header.PayloadLen, header.TrafficClass)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unknown packet, discarded(%d)", len(data))
|
||||
}
|
||||
|
||||
rid := routerID.String()
|
||||
|
||||
var route *router.Route
|
||||
if r := registry.RouterRegistry().Get(rid); r != nil {
|
||||
route = r.GetRoute(ctx, dstIP.String(), router.IDOption(rid))
|
||||
}
|
||||
if route == nil && h.md.router != nil {
|
||||
route = h.md.router.GetRoute(ctx, dstIP.String(), router.IDOption(rid))
|
||||
}
|
||||
if route == nil || route.Gateway == "" {
|
||||
// no route to host, discard
|
||||
return fmt.Errorf("route to host %s", dstIP)
|
||||
}
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
log.Tracef("route for %s: %s -> %s", dstIP, route.Dst, route.Gateway)
|
||||
}
|
||||
|
||||
if c := h.pool.Get(routerID, route.Gateway); c != nil {
|
||||
if w := c.Writer(); w != nil {
|
||||
w.Write(data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if h.md.sd == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
clientID := fmt.Sprintf("%s@%s", route.Gateway, routerID)
|
||||
ss, _ := h.md.sd.Get(ctx, clientID)
|
||||
|
||||
var service *sd.Service
|
||||
for _, s := range ss {
|
||||
if s.Node != h.id {
|
||||
service = s
|
||||
break
|
||||
}
|
||||
}
|
||||
if service == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
raddr, _ := net.ResolveUDPAddr("udp", service.Address)
|
||||
if raddr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
req := &relay.Request{
|
||||
Version: relay.Version1,
|
||||
Cmd: relay.CmdAssociate,
|
||||
Features: []relay.Feature{
|
||||
&relay.TunnelFeature{
|
||||
ID: routerID,
|
||||
},
|
||||
&relay.AddrFeature{
|
||||
AType: relay.AddrDomain,
|
||||
Host: route.Gateway,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
req.WriteTo(&buf)
|
||||
buf.Write(data)
|
||||
|
||||
h.epConn.WriteTo(buf.Bytes(), raddr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type packetConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/go-gost/core/common/bufpool"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/relay"
|
||||
)
|
||||
|
||||
func (h *routerHandler) handleEntrypoint(log logger.Logger) error {
|
||||
buf := bufpool.Get(h.md.bufferSize)
|
||||
defer bufpool.Put(buf)
|
||||
|
||||
for {
|
||||
err := func() error {
|
||||
n, addr, err := h.epConn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := relay.Request{}
|
||||
nn, err := req.ReadFrom(bytes.NewReader(buf[:n]))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if req.Cmd != relay.CmdAssociate {
|
||||
return nil
|
||||
}
|
||||
|
||||
var routerID relay.TunnelID
|
||||
var gateway string
|
||||
|
||||
for _, f := range req.Features {
|
||||
switch f.Type() {
|
||||
case relay.FeatureTunnel:
|
||||
if feature, _ := f.(*relay.TunnelFeature); feature != nil {
|
||||
routerID = feature.ID
|
||||
}
|
||||
case relay.FeatureAddr:
|
||||
if feature, _ := f.(*relay.AddrFeature); feature != nil {
|
||||
gateway = feature.Host
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Tracef("redirect from %s to %s@%s", addr, gateway, routerID)
|
||||
|
||||
if c := h.pool.Get(routerID, gateway); c != nil {
|
||||
if w := c.Writer(); w != nil {
|
||||
w.Write(buf[nn:])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
-18
@@ -3,6 +3,7 @@ package router
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -13,9 +14,9 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/observer"
|
||||
"github.com/go-gost/core/service"
|
||||
"github.com/go-gost/relay"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
limiter_util "github.com/go-gost/x/internal/util/limiter"
|
||||
stats_util "github.com/go-gost/x/internal/util/stats"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
@@ -24,12 +25,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadVersion = errors.New("bad version")
|
||||
ErrUnknownCmd = errors.New("unknown command")
|
||||
ErrRouterID = errors.New("invalid router ID")
|
||||
ErrRouterNotAvailable = errors.New("router not available")
|
||||
ErrUnauthorized = errors.New("unauthorized")
|
||||
ErrTunnelRoute = errors.New("no route to host")
|
||||
ErrBadVersion = errors.New("bad version")
|
||||
ErrUnknownCmd = errors.New("unknown command")
|
||||
ErrRouterID = errors.New("invalid router ID")
|
||||
ErrUnauthorized = errors.New("unauthorized")
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -40,7 +39,7 @@ type routerHandler struct {
|
||||
id string
|
||||
options handler.Options
|
||||
pool *ConnectorPool
|
||||
epSvc service.Service
|
||||
epConn net.PacketConn
|
||||
md metadata
|
||||
log logger.Logger
|
||||
stats *stats_util.HandlerStats
|
||||
@@ -75,14 +74,9 @@ func (h *routerHandler) Init(md md.Metadata) (err error) {
|
||||
})
|
||||
h.pool = NewConnectorPool(h.id)
|
||||
|
||||
/*
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.ep.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
*/
|
||||
if err = h.initEntrypoint(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
h.cancel = cancel
|
||||
@@ -103,6 +97,35 @@ func (h *routerHandler) Init(md md.Metadata) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *routerHandler) initEntrypoint() (err error) {
|
||||
if h.md.entryPoint == "" {
|
||||
return
|
||||
}
|
||||
|
||||
network := "udp"
|
||||
if xnet.IsIPv4(h.md.entryPoint) {
|
||||
network = "udp4"
|
||||
}
|
||||
|
||||
pc, err := net.ListenPacket(network, h.md.entryPoint)
|
||||
if err != nil {
|
||||
h.log.Error(err)
|
||||
return
|
||||
}
|
||||
h.epConn = pc
|
||||
|
||||
log := h.log.WithFields(map[string]any{
|
||||
"service": fmt.Sprintf("%s-ep-%s", h.options.Service, pc.LocalAddr()),
|
||||
"listener": "udp",
|
||||
"handler": "entrypoint",
|
||||
"kind": "service",
|
||||
})
|
||||
go h.handleEntrypoint(log)
|
||||
h.log.Infof("entrypoint: %s", pc.LocalAddr())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *routerHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
@@ -212,8 +235,8 @@ func (h *routerHandler) Handle(ctx context.Context, conn net.Conn, opts ...handl
|
||||
|
||||
// Close implements io.Closer interface.
|
||||
func (h *routerHandler) Close() error {
|
||||
if h.epSvc != nil {
|
||||
h.epSvc.Close()
|
||||
if h.epConn != nil {
|
||||
h.epConn.Close()
|
||||
}
|
||||
h.pool.Close()
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
const (
|
||||
defaultTTL = 15 * time.Second
|
||||
defaultBufferSize = 1500
|
||||
defaultBufferSize = 4096
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
|
||||
@@ -272,7 +272,7 @@ func (h *tunHandler) findRouteFor(ctx context.Context, dst net.IP, router router
|
||||
return nil
|
||||
}
|
||||
|
||||
if route := router.GetRoute(ctx, dst); route != nil {
|
||||
if route := router.GetRoute(ctx, dst.String()); route != nil {
|
||||
if gw := net.ParseIP(route.Gateway); gw != nil {
|
||||
if v, ok := h.routes.Load(ipToTunRouteKey(gw)); ok {
|
||||
return v.(net.Addr)
|
||||
|
||||
@@ -57,7 +57,7 @@ func (d *Dialer) Dial(ctx context.Context, network string, tid string) (conn net
|
||||
var service *sd.Service
|
||||
for _, s := range ss {
|
||||
d.log.Debugf("%+v", s)
|
||||
if s.Name != d.node && s.Network == network {
|
||||
if s.Node != d.node && s.Network == network {
|
||||
service = s
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user