update tap

This commit is contained in:
ginuerzh 2022-11-04 18:48:28 +08:00
parent 30d44c7376
commit 05ddda70e3
9 changed files with 181 additions and 92 deletions

View File

@ -102,7 +102,9 @@ func (h *tunHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
}) })
log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr) log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr)
h.handleClient(ctx, conn, raddr, config, log) if err := h.handleClient(ctx, conn, raddr, config, log); err != nil {
log.Error(err)
}
return nil return nil
} }

View File

@ -173,8 +173,6 @@ func (h *tunHandler) transportServer(tun net.Conn, conn net.PacketConn, config *
return nil return nil
} }
// h.updateRoute(src, addr, log)
if addr := h.findRouteFor(dst, config.Routes...); addr != nil { if addr := h.findRouteFor(dst, config.Routes...); addr != nil {
log.Debugf("find route: %s -> %s", dst, addr) log.Debugf("find route: %s -> %s", dst, addr)

View File

@ -1,9 +1,16 @@
package tap package tap
import "net"
// Route is an IP routing entry
type Route struct {
Net net.IPNet
Gateway net.IP
}
type Config struct { type Config struct {
Name string Name string
Net string Net string
MTU int MTU int
Routes []string
Gateway string Gateway string
Routes []Route
} }

View File

@ -1,18 +1,20 @@
package tap package tap
import ( import (
"context"
"errors" "errors"
"io"
"net" "net"
"time" "time"
mdata "github.com/go-gost/core/metadata" mdata "github.com/go-gost/core/metadata"
"github.com/songgao/water"
) )
type conn struct { type conn struct {
ifce *water.Interface ifce io.ReadWriteCloser
laddr net.Addr laddr net.Addr
raddr net.Addr raddr net.Addr
cancel context.CancelFunc
} }
func (c *conn) Read(b []byte) (n int, err error) { func (c *conn) Read(b []byte) (n int, err error) {
@ -44,6 +46,9 @@ func (c *conn) SetWriteDeadline(t time.Time) error {
} }
func (c *conn) Close() (err error) { func (c *conn) Close() (err error) {
if c.cancel != nil {
c.cancel()
}
return c.ifce.Close() return c.ifce.Close()
} }

View File

@ -1,7 +1,9 @@
package tap package tap
import ( import (
"context"
"net" "net"
"time"
"github.com/go-gost/core/listener" "github.com/go-gost/core/listener"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
@ -18,7 +20,6 @@ func init() {
} }
type tapListener struct { type tapListener struct {
saddr string
addr net.Addr addr net.Addr
cqueue chan net.Conn cqueue chan net.Conn
closed chan struct{} closed chan struct{}
@ -33,7 +34,6 @@ func NewListener(opts ...listener.Option) listener.Listener {
opt(&options) opt(&options)
} }
return &tapListener{ return &tapListener{
saddr: options.Addr,
logger: options.Logger, logger: options.Logger,
options: options, options: options,
} }
@ -48,48 +48,72 @@ func (l *tapListener) Init(md mdata.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "udp4" network = "udp4"
} }
l.addr, err = net.ResolveUDPAddr(network, l.saddr) l.addr, err = net.ResolveUDPAddr(network, l.options.Addr)
if err != nil { if err != nil {
return return
} }
ifce, ip, err := l.createTap()
if err != nil {
if ifce != nil {
ifce.Close()
}
return
}
itf, err := net.InterfaceByName(ifce.Name())
if err != nil {
return
}
addrs, _ := itf.Addrs()
l.logger.Infof("name: %s, mac: %s, mtu: %d, addrs: %s",
itf.Name, itf.HardwareAddr, itf.MTU, addrs)
l.cqueue = make(chan net.Conn, 1) l.cqueue = make(chan net.Conn, 1)
l.closed = make(chan struct{}) l.closed = make(chan struct{})
var c net.Conn go l.listenLoop()
c = &conn{
ifce: ifce,
laddr: l.addr,
raddr: &net.IPAddr{IP: ip},
}
c = metrics.WrapConn(l.options.Service, c)
c = limiter.WrapConn(l.options.TrafficLimiter, c)
c = withMetadata(mdx.NewMetadata(map[string]any{
"config": l.md.config,
}), c)
l.cqueue <- c
return return
} }
func (l *tapListener) listenLoop() {
for {
ctx, cancel := context.WithCancel(context.Background())
err := func() error {
ifce, name, ip, err := l.createTap()
if err != nil {
if ifce != nil {
ifce.Close()
}
return err
}
itf, err := net.InterfaceByName(name)
if err != nil {
return err
}
addrs, _ := itf.Addrs()
l.logger.Infof("name: %s, net: %s, mtu: %d, addrs: %s",
itf.Name, ip, itf.MTU, addrs)
var c net.Conn
c = &conn{
ifce: ifce,
laddr: l.addr,
raddr: &net.IPAddr{IP: ip},
cancel: cancel,
}
c = metrics.WrapConn(l.options.Service, c)
c = limiter.WrapConn(l.options.TrafficLimiter, c)
c = withMetadata(mdx.NewMetadata(map[string]any{
"config": l.md.config,
}), c)
l.cqueue <- c
return nil
}()
if err != nil {
l.logger.Error(err)
cancel()
}
select {
case <-ctx.Done():
case <-l.closed:
return
}
time.Sleep(time.Second)
}
}
func (l *tapListener) Accept() (net.Conn, error) { func (l *tapListener) Accept() (net.Conn, error) {
select { select {
case conn := <-l.cqueue: case conn := <-l.cqueue:

View File

@ -1,6 +1,9 @@
package tap package tap
import ( import (
"net"
"strings"
mdata "github.com/go-gost/core/metadata" mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util" mdutil "github.com/go-gost/core/metadata/util"
tap_util "github.com/go-gost/x/internal/util/tap" tap_util "github.com/go-gost/x/internal/util/tap"
@ -19,6 +22,7 @@ func (l *tapListener) parseMetadata(md mdata.Metadata) (err error) {
name = "name" name = "name"
netKey = "net" netKey = "net"
mtu = "mtu" mtu = "mtu"
route = "route"
routes = "routes" routes = "routes"
gateway = "gw" gateway = "gw"
) )
@ -33,9 +37,35 @@ func (l *tapListener) parseMetadata(md mdata.Metadata) (err error) {
config.MTU = DefaultMTU config.MTU = DefaultMTU
} }
gw := net.ParseIP(config.Gateway)
for _, s := range strings.Split(mdutil.GetString(md, route), ",") {
var route tap_util.Route
_, ipNet, _ := net.ParseCIDR(strings.TrimSpace(s))
if ipNet == nil {
continue
}
route.Net = *ipNet
route.Gateway = gw
config.Routes = append(config.Routes, route)
}
for _, s := range mdutil.GetStrings(md, routes) { for _, s := range mdutil.GetStrings(md, routes) {
if s != "" { ss := strings.SplitN(s, " ", 2)
config.Routes = append(config.Routes, s) if len(ss) == 2 {
var route tap_util.Route
_, ipNet, _ := net.ParseCIDR(strings.TrimSpace(ss[0]))
if ipNet == nil {
continue
}
route.Net = *ipNet
route.Gateway = net.ParseIP(ss[1])
if route.Gateway == nil {
route.Gateway = gw
}
config.Routes = append(config.Routes, route)
} }
} }

View File

@ -2,15 +2,16 @@ package tap
import ( import (
"fmt" "fmt"
"io"
"net" "net"
"os/exec"
"strings"
tap_util "github.com/go-gost/x/internal/util/tap"
"github.com/songgao/water" "github.com/songgao/water"
"github.com/vishvananda/netlink"
) )
func (l *tapListener) createTap() (ifce *water.Interface, ip net.IP, err error) { func (l *tapListener) createTap() (dev io.ReadWriteCloser, name string, ip net.IP, err error) {
ifce, err = water.New(water.Config{ tap, err := water.New(water.Config{
DeviceType: water.TAP, DeviceType: water.TAP,
PlatformSpecificParams: water.PlatformSpecificParams{ PlatformSpecificParams: water.PlatformSpecificParams{
Name: l.md.config.Name, Name: l.md.config.Name,
@ -20,46 +21,61 @@ func (l *tapListener) createTap() (ifce *water.Interface, ip net.IP, err error)
return return
} }
if err = l.exeCmd(fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), l.md.config.MTU)); err != nil { dev = tap
l.logger.Warn(err) name = tap.Name()
ifce, err := net.InterfaceByName(name)
if err != nil {
return
}
link, err := netlink.LinkByName(name)
if err != nil {
return
}
if err = netlink.LinkSetMTU(link, l.md.config.MTU); err != nil {
return
} }
if l.md.config.Net != "" { if l.md.config.Net != "" {
if err = l.exeCmd(fmt.Sprintf("ip address add %s dev %s", l.md.config.Net, ifce.Name())); err != nil { var ipNet *net.IPNet
l.logger.Warn(err) ip, ipNet, err = net.ParseCIDR(l.md.config.Net)
if err != nil {
return
}
if err = netlink.AddrAdd(link, &netlink.Addr{
IPNet: &net.IPNet{
IP: ip,
Mask: ipNet.Mask,
},
}); err != nil {
return
} }
} }
if err = netlink.LinkSetUp(link); err != nil {
if err = l.exeCmd(fmt.Sprintf("ip link set dev %s up", ifce.Name())); err != nil { return
l.logger.Warn(err)
} }
if err = l.addRoutes(ifce.Name(), l.md.config.Gateway, l.md.config.Routes...); err != nil { if err = l.addRoutes(ifce, l.md.config.Routes...); err != nil {
return return
} }
return return
} }
func (l *tapListener) exeCmd(cmd string) error { func (l *tapListener) addRoutes(ifce *net.Interface, routes ...tap_util.Route) error {
l.logger.Debug(cmd)
args := strings.Split(cmd, " ")
if err := exec.Command(args[0], args[1:]...).Run(); err != nil {
return fmt.Errorf("%s: %v", cmd, err)
}
return nil
}
func (l *tapListener) addRoutes(ifName string, gw string, routes ...string) error {
for _, route := range routes { for _, route := range routes {
cmd := fmt.Sprintf("ip route add %s via %s dev %s", route, gw, ifName) r := netlink.Route{
l.logger.Debug(cmd) Dst: &route.Net,
Gw: route.Gateway,
args := strings.Split(cmd, " ") }
if er := exec.Command(args[0], args[1:]...).Run(); er != nil { if r.Gw == nil {
l.logger.Warnf("%s: %v", cmd, er) r.LinkIndex = ifce.Index
}
if err := netlink.RouteReplace(&r); err != nil {
return fmt.Errorf("add route %v %v: %v", r.Dst, r.Gw, err)
} }
} }
return nil return nil

View File

@ -4,23 +4,28 @@ package tap
import ( import (
"fmt" "fmt"
"io"
"net" "net"
"os/exec" "os/exec"
"strings" "strings"
tap_util "github.com/go-gost/x/internal/util/tap"
"github.com/songgao/water" "github.com/songgao/water"
) )
func (l *tapListener) createTap() (ifce *water.Interface, ip net.IP, err error) { func (l *tapListener) createTap() (dev io.ReadWriteCloser, name string, ip net.IP, err error) {
ip, _, _ = net.ParseCIDR(l.md.config.Net) ip, _, _ = net.ParseCIDR(l.md.config.Net)
ifce, err = water.New(water.Config{ ifce, err := water.New(water.Config{
DeviceType: water.TAP, DeviceType: water.TAP,
}) })
if err != nil { if err != nil {
return return
} }
dev = ifce
name = ifce.Name()
var cmd string var cmd string
if l.md.config.Net != "" { if l.md.config.Net != "" {
cmd = fmt.Sprintf("ifconfig %s inet %s mtu %d up", ifce.Name(), l.md.config.Net, l.md.config.MTU) cmd = fmt.Sprintf("ifconfig %s inet %s mtu %d up", ifce.Name(), l.md.config.Net, l.md.config.MTU)
@ -35,21 +40,18 @@ func (l *tapListener) createTap() (ifce *water.Interface, ip net.IP, err error)
return return
} }
if err = l.addRoutes(ifce.Name(), l.md.config.Gateway, l.md.config.Routes...); err != nil { if err = l.addRoutes(ifce.Name(), l.md.config.Routes...); err != nil {
return return
} }
return return
} }
func (l *tapListener) addRoutes(ifName string, gw string, routes ...string) error { func (l *tapListener) addRoutes(ifName string, routes ...tap_util.Route) error {
for _, route := range routes { for _, route := range routes {
if route == "" { cmd := fmt.Sprintf("route add -net %s dev %s", route.Net.String(), ifName)
continue if route.Gateway != nil {
} cmd += " gw " + route.Gateway.String()
cmd := fmt.Sprintf("route add -net %s dev %s", route, ifName)
if gw != "" {
cmd += " gw " + gw
} }
l.logger.Debug(cmd) l.logger.Debug(cmd)
args := strings.Split(cmd, " ") args := strings.Split(cmd, " ")

View File

@ -2,17 +2,19 @@ package tap
import ( import (
"fmt" "fmt"
"io"
"net" "net"
"os/exec" "os/exec"
"strings" "strings"
tap_util "github.com/go-gost/x/internal/util/tap"
"github.com/songgao/water" "github.com/songgao/water"
) )
func (l *tapListener) createTap() (ifce *water.Interface, ip net.IP, err error) { func (l *tapListener) createTap() (dev io.ReadWriteCloser, name string, ip net.IP, err error) {
ip, ipNet, _ := net.ParseCIDR(l.md.config.Net) ip, ipNet, _ := net.ParseCIDR(l.md.config.Net)
ifce, err = water.New(water.Config{ ifce, err := water.New(water.Config{
DeviceType: water.TAP, DeviceType: water.TAP,
PlatformSpecificParams: water.PlatformSpecificParams{ PlatformSpecificParams: water.PlatformSpecificParams{
ComponentID: "tap0901", ComponentID: "tap0901",
@ -24,6 +26,9 @@ func (l *tapListener) createTap() (ifce *water.Interface, ip net.IP, err error)
return return
} }
dev = ifce
name = ifce.Name()
if ip != nil && ipNet != nil { if ip != nil && ipNet != nil {
cmd := fmt.Sprintf("netsh interface ip set address name=%s "+ cmd := fmt.Sprintf("netsh interface ip set address name=%s "+
"source=static addr=%s mask=%s gateway=none", "source=static addr=%s mask=%s gateway=none",
@ -37,21 +42,21 @@ func (l *tapListener) createTap() (ifce *water.Interface, ip net.IP, err error)
} }
} }
if err = l.addRoutes(ifce.Name(), l.md.config.Gateway, l.md.config.Routes...); err != nil { if err = l.addRoutes(ifce.Name(), l.md.config.Routes...); err != nil {
return return
} }
return return
} }
func (l *tapListener) addRoutes(ifName string, gw string, routes ...string) error { func (l *tapListener) addRoutes(ifName string, routes ...tap_util.Route) error {
for _, route := range routes { for _, route := range routes {
l.deleteRoute(ifName, route) l.deleteRoute(ifName, route)
cmd := fmt.Sprintf("netsh interface ip add route prefix=%s interface=%s store=active", cmd := fmt.Sprintf("netsh interface ip add route prefix=%s interface=%s store=active",
route, ifName) route.Net.String(), ifName)
if gw != "" { if route.Gateway != nil {
cmd += " nexthop=" + gw cmd += " nexthop=" + route.Gateway.String()
} }
l.logger.Debug(cmd) l.logger.Debug(cmd)
args := strings.Split(cmd, " ") args := strings.Split(cmd, " ")
@ -62,9 +67,9 @@ func (l *tapListener) addRoutes(ifName string, gw string, routes ...string) erro
return nil return nil
} }
func (l *tapListener) deleteRoute(ifName string, route string) error { func (l *tapListener) deleteRoute(ifName string, route tap_util.Route) error {
cmd := fmt.Sprintf("netsh interface ip delete route prefix=%s interface=%s store=active", cmd := fmt.Sprintf("netsh interface ip delete route prefix=%s interface=%s store=active",
route, ifName) route.Net.String(), ifName)
l.logger.Debug(cmd) l.logger.Debug(cmd)
args := strings.Split(cmd, " ") args := strings.Split(cmd, " ")
return exec.Command(args[0], args[1:]...).Run() return exec.Command(args[0], args[1:]...).Run()