add support for tap device

This commit is contained in:
ginuerzh 2020-01-01 21:30:36 +08:00
parent 355847faa4
commit bde4217ca9
8 changed files with 504 additions and 50 deletions

View File

@ -396,6 +396,8 @@ func (r *route) GenRouters() ([]router, error) {
ln, err = gost.ObfsHTTPListener(node.Addr)
case "tun":
ln, err = gost.TunListener(node.Addr)
case "tap":
ln, err = gost.TapListener(node.Addr)
default:
ln, err = gost.TCPListener(node.Addr)
}
@ -441,6 +443,14 @@ func (r *route) GenRouters() ([]router, error) {
Routes: strings.Split(node.Get("route"), ","),
}
handler = gost.TunHandler(node.Remote, gost.TunConfigHandlerOption(cfg))
case "tap":
cfg := gost.TapConfig{
Name: node.Get("name"),
Addr: node.Get("net"),
MTU: node.GetInt("mtu"),
Routes: strings.Split(node.Get("route"), ","),
}
handler = gost.TapHandler(node.Remote, gost.TapConfigHandlerOption(cfg))
default:
// start from 2.5, if remote is not empty, then we assume that it is a forward tunnel.
if node.Remote != "" {

View File

@ -41,6 +41,7 @@ type HandlerOptions struct {
Host string
IPs []string
TunConfig TunConfig
TapConfig TapConfig
}
// HandlerOption allows a common way to set handler options.
@ -203,6 +204,13 @@ func TunConfigHandlerOption(cfg TunConfig) HandlerOption {
}
}
// TapConfigHandlerOption sets the config for tap device.
func TapConfigHandlerOption(cfg TapConfig) HandlerOption {
return func(opts *HandlerOptions) {
opts.TapConfig = cfg
}
}
type autoHandler struct {
options *HandlerOptions
}

View File

@ -82,7 +82,7 @@ func ParseNode(s string) (node Node, err error) {
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
case "rtcp", "rudp": // rtcp and rudp are for remote port forwarding
case "ohttp": // obfs-http
case "tun": //tun device
case "tun", "tap": // tun/tap device
default:
node.Transport = "tcp"
}
@ -94,7 +94,7 @@ func ParseNode(s string) (node Node, err error) {
case "tcp", "udp", "rtcp", "rudp": // port forwarding
case "direct", "remote", "forward": // forwarding
case "redirect": // TCP transparent proxy
case "tun": // tun device
case "tun", "tap": // tun/tap device
default:
node.Protocol = ""
}

View File

@ -11,6 +11,7 @@ import (
"github.com/go-log/log"
"github.com/shadowsocks/go-shadowsocks2/core"
"github.com/songgao/water"
"github.com/songgao/water/waterutil"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
@ -227,43 +228,6 @@ func (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.A
return err
}
type tunConn struct {
ifce *water.Interface
addr net.Addr
}
func (c *tunConn) Read(b []byte) (n int, err error) {
return c.ifce.Read(b)
}
func (c *tunConn) Write(b []byte) (n int, err error) {
return c.ifce.Write(b)
}
func (c *tunConn) Close() (err error) {
return c.ifce.Close()
}
func (c *tunConn) LocalAddr() net.Addr {
return c.addr
}
func (c *tunConn) RemoteAddr() net.Addr {
return &net.IPAddr{}
}
func (c *tunConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tun", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *tunConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tun", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *tunConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tun", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
type tunListener struct {
addr net.Addr
conns chan net.Conn
@ -318,3 +282,272 @@ func (l *tunListener) Close() error {
}
return nil
}
type TapConfig struct {
Name string
Addr string
MTU int
Routes []string
}
type tapHandler struct {
raddr string
options *HandlerOptions
ipNet *net.IPNet
routes sync.Map
}
// TapHandler creates a handler for tap tunnel.
func TapHandler(raddr string, opts ...HandlerOption) Handler {
h := &tapHandler{
raddr: raddr,
options: &HandlerOptions{},
}
for _, opt := range opts {
opt(h.options)
}
return h
}
func (h *tapHandler) Init(options ...HandlerOption) {
if h.options == nil {
h.options = &HandlerOptions{}
}
for _, opt := range options {
opt(h.options)
}
}
func (h *tapHandler) Handle(conn net.Conn) {
defer os.Exit(0)
defer conn.Close()
uc, ok := conn.(net.PacketConn)
if !ok {
log.Log("[tap] wrong connection type, must be PacketConn")
return
}
tc, err := h.createTap()
if err != nil {
log.Logf("[tap] %s create tap: %v", conn.LocalAddr(), err)
return
}
defer tc.Close()
log.Logf("[tap] %s - %s: tap creation successful", tc.LocalAddr(), conn.LocalAddr())
var raddr net.Addr
if h.raddr != "" {
raddr, err = net.ResolveUDPAddr("udp", h.raddr)
if err != nil {
log.Logf("[tap] %s - %s remote addr: %v", tc.LocalAddr(), conn.LocalAddr(), err)
return
}
}
if len(h.options.Users) > 0 && h.options.Users[0] != nil {
passwd, _ := h.options.Users[0].Password()
cipher, err := core.PickCipher(h.options.Users[0].Username(), nil, passwd)
if err != nil {
log.Logf("[tap] %s - %s cipher: %v", tc.LocalAddr(), conn.LocalAddr(), err)
return
}
uc = cipher.PacketConn(uc)
}
h.transportTap(tc, uc, raddr)
}
func (h *tapHandler) createTap() (conn net.Conn, err error) {
conn, h.ipNet, err = createTap(h.options.TapConfig)
return
}
func (h *tapHandler) transportTap(tap net.Conn, conn net.PacketConn, raddr net.Addr) error {
errc := make(chan error, 1)
go func() {
for {
err := func() error {
b := sPool.Get().([]byte)
defer sPool.Put(b)
n, err := tap.Read(b)
if err != nil {
return err
}
macSrc := waterutil.MACSource(b[:n])
macDst := waterutil.MACDestination(b[:n])
addr := raddr
if v, ok := h.routes.Load(macDst.String()); ok {
addr = v.(net.Addr)
}
if addr == nil {
log.Logf("[tap] %s: no route for %s -> %s %d %d",
tap.LocalAddr(), macSrc, macDst, n, waterutil.MACEthertype(b[:n]))
return nil
}
if Debug {
log.Logf("[tap] %s >>> %s: %s -> %s %d %d",
tap.LocalAddr(), addr, macSrc, macDst, n, waterutil.MACEthertype(b[:n]))
}
if _, err := conn.WriteTo(b[:n], addr); err != nil {
return err
}
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
go func() {
for {
err := func() error {
b := sPool.Get().([]byte)
defer mPool.Put(b)
n, addr, err := conn.ReadFrom(b)
if err != nil {
return err
}
macSrc := waterutil.MACSource(b[:n])
macDst := waterutil.MACDestination(b[:n])
if Debug {
log.Logf("[tap] %s <<< %s: %s -> %s %d %d",
tap.LocalAddr(), addr, macSrc, macDst, n, waterutil.MACEthertype(b[:n]))
}
if actual, loaded := h.routes.LoadOrStore(macSrc.String(), addr); loaded {
if actual.(net.Addr).String() != addr.String() {
log.Logf("[tap] %s <- %s: update route: %s -> %s (old %s)",
tap.LocalAddr(), addr, macSrc, addr, actual.(net.Addr))
h.routes.Store(macSrc.String(), addr)
}
} else {
log.Logf("[tap] %s: new route: %s -> %s", tap.LocalAddr(), macSrc, addr)
}
if _, err := tap.Write(b[:n]); err != nil {
return err
}
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
err := <-errc
if err != nil && err == io.EOF {
err = nil
}
log.Logf("[tap] %s - %s: %v", tap.LocalAddr(), conn.LocalAddr(), err)
return err
}
type tapListener struct {
addr net.Addr
conns chan net.Conn
closed chan struct{}
}
// TapListener creates a listener for tap tunnel.
func TapListener(addr string) (Listener, error) {
laddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
threads := 1
ln := &tapListener{
addr: laddr,
conns: make(chan net.Conn, threads),
closed: make(chan struct{}),
}
for i := 0; i < threads; i++ {
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
return nil, err
}
ln.conns <- conn
}
return ln, nil
}
func (l *tapListener) Accept() (net.Conn, error) {
select {
case conn := <-l.conns:
return conn, nil
case <-l.closed:
}
return nil, errors.New("accept on closed listener")
}
func (l *tapListener) Addr() net.Addr {
return l.addr
}
func (l *tapListener) Close() error {
select {
case <-l.closed:
return errors.New("listener has been closed")
default:
close(l.closed)
}
return nil
}
type tunTapConn struct {
ifce *water.Interface
addr net.Addr
}
func (c *tunTapConn) Read(b []byte) (n int, err error) {
return c.ifce.Read(b)
}
func (c *tunTapConn) Write(b []byte) (n int, err error) {
return c.ifce.Write(b)
}
func (c *tunTapConn) Close() (err error) {
return c.ifce.Close()
}
func (c *tunTapConn) LocalAddr() net.Addr {
return c.addr
}
func (c *tunTapConn) RemoteAddr() net.Addr {
return &net.IPAddr{}
}
func (c *tunTapConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *tunTapConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *tunTapConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

View File

@ -1,8 +1,7 @@
// +build !linux,!windows
package gost
import (
"errors"
"fmt"
"net"
"os/exec"
@ -42,13 +41,18 @@ func createTun(cfg TunConfig) (conn net.Conn, ipNet *net.IPNet, err error) {
return
}
conn = &tunConn{
conn = &tunTapConn{
ifce: ifce,
addr: &net.IPAddr{IP: ip},
}
return
}
func createTap(cfg TapConfig) (conn net.Conn, ipNet *net.IPNet, err error) {
err = errors.New("tap is not supported on darwin")
return
}
func addRoutes(ifName string, routes ...string) error {
for _, route := range routes {
if route == "" {

View File

@ -57,24 +57,82 @@ func createTun(cfg TunConfig) (conn net.Conn, ipNet *net.IPNet, err error) {
return
}
if err = addRoutes(ifce.Name(), cfg.Routes...); err != nil {
if err = addRoutes("tun", ifce.Name(), cfg.Routes...); err != nil {
return
}
conn = &tunConn{
conn = &tunTapConn{
ifce: ifce,
addr: &net.IPAddr{IP: ip},
}
return
}
func addRoutes(ifName string, routes ...string) error {
func createTap(cfg TapConfig) (conn net.Conn, ipNet *net.IPNet, err error) {
ip, ipNet, err := net.ParseCIDR(cfg.Addr)
if err != nil {
return
}
ifce, err := water.New(water.Config{
DeviceType: water.TAP,
PlatformSpecificParams: water.PlatformSpecificParams{
Name: cfg.Name,
},
})
if err != nil {
return
}
link, err := tenus.NewLinkFrom(ifce.Name())
if err != nil {
return
}
mtu := cfg.MTU
if mtu <= 0 {
mtu = DefaultMTU
}
cmd := fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu)
log.Log("[tap]", cmd)
if er := link.SetLinkMTU(mtu); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
cmd = fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name())
log.Log("[tap]", cmd)
if er := link.SetLinkIp(ip, ipNet); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
cmd = fmt.Sprintf("ip link set dev %s up", ifce.Name())
log.Log("[tap]", cmd)
if er := link.SetLinkUp(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
if err = addRoutes("tap", ifce.Name(), cfg.Routes...); err != nil {
return
}
conn = &tunTapConn{
ifce: ifce,
addr: &net.IPAddr{IP: ip},
}
return
}
func addRoutes(ifType, ifName string, routes ...string) error {
for _, route := range routes {
if route == "" {
continue
}
cmd := fmt.Sprintf("ip route add %s dev %s", route, ifName)
log.Log("[tun]", cmd)
log.Logf("[%s] %s", ifType, cmd)
if err := netlink.AddRoute(route, "", "", ifName); err != nil {
return fmt.Errorf("%s: %v", cmd, err)
}

102
tuntap_unix.go Normal file
View File

@ -0,0 +1,102 @@
// +build !linux,!windows,!darwin
package gost
import (
"fmt"
"net"
"os/exec"
"strings"
"github.com/go-log/log"
"github.com/songgao/water"
)
func createTun(cfg TunConfig) (conn net.Conn, ipNet *net.IPNet, err error) {
ip, ipNet, err := net.ParseCIDR(cfg.Addr)
if err != nil {
return
}
ifce, err := water.New(water.Config{
DeviceType: water.TUN,
})
if err != nil {
return
}
mtu := cfg.MTU
if mtu <= 0 {
mtu = DefaultMTU
}
cmd := fmt.Sprintf("ifconfig %s inet %s mtu %d up", ifce.Name(), cfg.Addr, mtu)
log.Log("[tun]", cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
if err = addRoutes("tun", ifce.Name(), cfg.Routes...); err != nil {
return
}
conn = &tunTapConn{
ifce: ifce,
addr: &net.IPAddr{IP: ip},
}
return
}
func createTap(cfg TapConfig) (conn net.Conn, ipNet *net.IPNet, err error) {
ip, ipNet, err := net.ParseCIDR(cfg.Addr)
if err != nil {
return
}
ifce, err := water.New(water.Config{
DeviceType: water.TAP,
})
if err != nil {
return
}
mtu := cfg.MTU
if mtu <= 0 {
mtu = DefaultMTU
}
cmd := fmt.Sprintf("ifconfig %s inet %s mtu %d up", ifce.Name(), cfg.Addr, mtu)
log.Log("[tap]", cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
if err = addRoutes("tap", ifce.Name(), cfg.Routes...); err != nil {
return
}
conn = &tunTapConn{
ifce: ifce,
addr: &net.IPAddr{IP: ip},
}
return
}
func addRoutes(ifType, ifName string, routes ...string) error {
for _, route := range routes {
if route == "" {
continue
}
cmd := fmt.Sprintf("route add -net %s -interface %s", route, ifName)
log.Logf("[%s] %s", ifType, cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
return fmt.Errorf("%s: %v", cmd, er)
}
}
return nil
}

View File

@ -38,18 +38,57 @@ func createTun(cfg TunConfig) (conn net.Conn, ipNet *net.IPNet, err error) {
return
}
if err = addRoutes(ifce.Name(), cfg.Routes...); err != nil {
if err = addRoutes("tun", ifce.Name(), cfg.Routes...); err != nil {
return
}
conn = &tunConn{
conn = &tunTapConn{
ifce: ifce,
addr: &net.IPAddr{IP: ip},
}
return
}
func addRoutes(ifName string, routes ...string) error {
func createTap(cfg TapConfig) (conn net.Conn, ipNet *net.IPNet, err error) {
ip, ipNet, err := net.ParseCIDR(cfg.Addr)
if err != nil {
return
}
ifce, err := water.New(water.Config{
DeviceType: water.TAP,
PlatformSpecificParams: water.PlatformSpecificParams{
ComponentID: "tap0901",
InterfaceName: cfg.Name,
Network: cfg.Addr,
},
})
if err != nil {
return
}
cmd := fmt.Sprintf("netsh interface ip set address name=%s "+
"source=static addr=%s mask=%s gateway=none",
ifce.Name(), ip.String(), ipMask(ipNet.Mask))
log.Log("[tap]", cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
if err = addRoutes("tap", ifce.Name(), cfg.Routes...); err != nil {
return
}
conn = &tunTapConn{
ifce: ifce,
addr: &net.IPAddr{IP: ip},
}
return
}
func addRoutes(ifType, ifName string, routes ...string) error {
for _, route := range routes {
if route == "" {
continue
@ -59,7 +98,7 @@ func addRoutes(ifName string, routes ...string) error {
cmd := fmt.Sprintf("netsh interface ip add route prefix=%s interface=%s store=active",
route, ifName)
log.Log("[tun]", cmd)
log.Logf("[%s] %s", ifType, cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
return fmt.Errorf("%s: %v", cmd, er)