a udp tunnel over tcp

This commit is contained in:
p_caiwfeng
2021-10-22 09:54:44 +08:00
parent d317d96cf1
commit a38e77f067
3 changed files with 46 additions and 236 deletions

View File

@@ -65,17 +65,12 @@ func (c *Chain) DialContext(ctx context.Context, network, address string) (conn
}
func (c *Chain) dial(ctx context.Context, network, address string) (net.Conn, error) {
route, err := c.selectRoute()
if err != nil {
return nil, err
}
ipAddr := address
if address != "" {
ipAddr = c.resolve(address)
}
if route.IsEmpty() {
if c.IsEmpty() {
switch network {
case "udp", "udp4", "udp6":
if address == "" {
@@ -90,12 +85,12 @@ func (c *Chain) dial(ctx context.Context, network, address string) (net.Conn, er
return d.DialContext(ctx, network, ipAddr)
}
conn, err := route.getConn(ctx)
conn, err := c.getConn(ctx)
if err != nil {
return nil, err
}
cc, err := route.Node().Client.ConnectContext(ctx, conn, network, ipAddr)
cc, err := c.Node().Client.ConnectContext(ctx, conn, network, ipAddr)
if err != nil {
conn.Close()
return nil, err
@@ -122,12 +117,7 @@ func (c *Chain) Conn() (conn net.Conn, err error) {
}
for i := 0; i < retries; i++ {
var route *Chain
route, err = c.selectRoute()
if err != nil {
continue
}
conn, err = route.getConn(ctx)
conn, err = c.getConn(ctx)
if err == nil {
break
}
@@ -150,15 +140,3 @@ func (c *Chain) getConn(_ context.Context) (conn net.Conn, err error) {
conn = cc
return
}
func (c *Chain) selectRoute() (route *Chain, err error) {
if c.IsEmpty() {
return newRoute(), nil
}
if c.isRoute {
return c, nil
}
route = newRoute()
route.SetNode(c.node)
return
}

View File

@@ -3,7 +3,6 @@ package core
import (
"bytes"
"context"
"errors"
"fmt"
"github.com/wencaiwulue/kubevpn/util"
"net"
@@ -14,136 +13,50 @@ import (
log "github.com/sirupsen/logrus"
)
type clientSelector struct {
methods []uint8
}
func (selector *clientSelector) Methods() []uint8 {
if util.Debug {
log.Debug("[socks5] methods:", selector.methods)
}
return selector.methods
}
func (selector *clientSelector) AddMethod(methods ...uint8) {
selector.methods = append(selector.methods, methods...)
}
func (selector *clientSelector) Select(methods ...uint8) (method uint8) {
return
}
func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
if util.Debug {
log.Debug("[socks5] method selected:", method)
}
switch method {
case gosocks5.MethodNoAuth:
log.Debug("[socks5] client", "no auth")
return conn, nil
}
return conn, nil
}
type serverSelector struct {
methods []uint8
}
func (selector *serverSelector) Methods() []uint8 {
return selector.methods
}
func (selector *serverSelector) AddMethod(methods ...uint8) {
selector.methods = append(selector.methods, methods...)
}
func (selector *serverSelector) Select(methods ...uint8) (method uint8) {
if util.Debug {
log.Debugf("[socks5] %d %d %v", gosocks5.Ver5, len(methods), methods)
}
method = gosocks5.MethodNoAuth
return
}
func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
if util.Debug {
log.Debugf("[socks5] %d %d", gosocks5.Ver5, method)
}
switch method {
case gosocks5.MethodNoAuth:
log.Debugf("[socks5] server, no auth")
return conn, nil
case gosocks5.MethodNoAcceptable:
return nil, gosocks5.ErrBadMethod
}
return conn, nil
}
type socks5UDPTunConnector struct {
type fakeUDPTunConnector struct {
}
// SOCKS5UDPTunConnector creates a connector for SOCKS5 UDP-over-TCP relay.
// It accepts an optional auth info for SOCKS5 Username/Password Authentication.
func SOCKS5UDPTunConnector() Connector {
return &socks5UDPTunConnector{}
return &fakeUDPTunConnector{}
}
func (c *socks5UDPTunConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string) (net.Conn, error) {
func (c *fakeUDPTunConnector) ConnectContext(_ context.Context, conn net.Conn, network, address string) (net.Conn, error) {
switch network {
case "tcp", "tcp4", "tcp6":
return nil, fmt.Errorf("%s unsupported", network)
}
conn.SetDeadline(time.Now().Add(util.ConnectTimeout))
_ = conn.SetDeadline(time.Now().Add(util.ConnectTimeout))
defer conn.SetDeadline(time.Time{})
targetAddr, _ := net.ResolveUDPAddr("udp", address)
return newSocks5UDPTunnelConn(conn, nil, targetAddr)
return newFakeUDPTunnelConnOverTcp(conn, targetAddr)
}
type socks5Handler struct {
selector *serverSelector
type fakeUdpHandler struct {
}
// SOCKS5Handler creates a server Handler for SOCKS5 proxy server.
func SOCKS5Handler() Handler {
h := &socks5Handler{}
h := &fakeUdpHandler{}
h.Init()
return h
}
func (h *socks5Handler) Init(...HandlerOption) {
h.selector = &serverSelector{}
h.selector.AddMethod(
gosocks5.MethodNoAuth,
)
func (h *fakeUdpHandler) Init(...HandlerOption) {
}
func (h *socks5Handler) Handle(conn net.Conn) {
func (h *fakeUdpHandler) Handle(conn net.Conn) {
defer conn.Close()
conn = gosocks5.ServerConn(conn, h.selector)
req, err := gosocks5.ReadRequest(conn)
if err != nil {
log.Debugf("[socks5] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
return
}
if util.Debug {
log.Debugf("[socks5] %s -> %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), req)
}
switch req.Cmd {
case gosocks5.CmdUdp:
h.handleUDPTunnel(conn, req)
default:
log.Debugf("[socks5] %s - %s : Unrecognized request: %d", conn.RemoteAddr(), conn.LocalAddr(), req.Cmd)
log.Debugf("[socks5] %s -> %s\n", conn.RemoteAddr(), conn.LocalAddr())
}
h.handleUDPTunnel(conn)
}
func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {
func (h *fakeUdpHandler) transportUDP(relay, peer net.PacketConn) (err error) {
errc := make(chan error, 2)
var clientAddr net.Addr
@@ -196,7 +109,7 @@ func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {
}
buf := bytes.Buffer{}
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), b[:n])
dgram.Write(&buf)
_ = dgram.Write(&buf)
if _, err := relay.WriteTo(buf.Bytes(), clientAddr); err != nil {
errc <- err
return
@@ -207,15 +120,10 @@ func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {
}
}()
select {
case err = <-errc:
//log.Println("w exit", err)
}
return
return <-errc
}
func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error) {
func (h *fakeUdpHandler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error) {
errc := make(chan error, 2)
var clientAddr *net.UDPAddr
@@ -271,7 +179,7 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
dgram.Header.Rsv = 0
buf := bytes.Buffer{}
dgram.Write(&buf)
_ = dgram.Write(&buf)
if _, err := uc.WriteToUDP(buf.Bytes(), clientAddr); err != nil {
errc <- err
return
@@ -282,42 +190,28 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
}
}()
select {
case err = <-errc:
}
return
return <-errc
}
func (h *socks5Handler) handleUDPTunnel(conn net.Conn, req *gosocks5.Request) {
func (h *fakeUdpHandler) handleUDPTunnel(conn net.Conn) {
// serve tunnel udp, tunnel <-> remote, handle tunnel udp request
addr := req.Addr.String()
bindAddr, _ := net.ResolveUDPAddr("udp", addr)
bindAddr, _ := net.ResolveUDPAddr("udp", ":0")
uc, err := net.ListenUDP("udp", bindAddr)
if err != nil {
log.Debugf("[socks5] udp-tun %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)
log.Debugf("[socks5] udp-tun %s -> %s : %s", conn.RemoteAddr(), bindAddr, err)
return
}
defer uc.Close()
socksAddr := toSocksAddr(uc.LocalAddr())
socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
if err := reply.Write(conn); err != nil {
log.Debugf("[socks5] udp-tun %s <- %s : %s", conn.RemoteAddr(), socksAddr, err)
return
}
if util.Debug {
log.Debugf("[socks5] udp-tun %s <- %s\n%s", conn.RemoteAddr(), socksAddr, reply)
log.Debugf("[socks5] udp-tun %s <- %s\n", conn.RemoteAddr(), uc.LocalAddr())
}
log.Debugf("[socks5] udp-tun %s <-> %s", conn.RemoteAddr(), socksAddr)
h.tunnelServerUDP(conn, uc)
log.Debugf("[socks5] udp-tun %s >-< %s", conn.RemoteAddr(), socksAddr)
log.Debugf("[socks5] udp-tun %s <-> %s", conn.RemoteAddr(), uc.LocalAddr())
_ = h.tunnelServerUDP(conn, uc)
log.Debugf("[socks5] udp-tun %s >-< %s", conn.RemoteAddr(), uc.LocalAddr())
return
}
func (h *socks5Handler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err error) {
func (h *fakeUdpHandler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err error) {
errc := make(chan error, 2)
go func() {
@@ -333,8 +227,7 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err err
}
// pipe from peer to tunnel
dgram := gosocks5.NewUDPDatagram(
gosocks5.NewUDPHeader(uint16(n), 0, toSocksAddr(addr)), b[:n])
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, toSocksAddr(addr)), b[:n])
if err := dgram.Write(cc); err != nil {
log.Debugf("[socks5] udp-tun %s <- %s : %s", cc.RemoteAddr(), dgram.Header.Addr, err)
errc <- err
@@ -371,14 +264,9 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err err
}
}()
select {
case err = <-errc:
}
return
return <-errc
}
// TODO: support ipv6 and domain
func toSocksAddr(addr net.Addr) *gosocks5.Addr {
host := "0.0.0.0"
port := 0
@@ -394,71 +282,26 @@ func toSocksAddr(addr net.Addr) *gosocks5.Addr {
}
}
func socks5Handshake(conn net.Conn) (net.Conn, error) {
cs := &clientSelector{}
cs.AddMethod(
gosocks5.MethodNoAuth,
)
cc := gosocks5.ClientConn(conn, cs)
if err := cc.Handleshake(); err != nil {
return nil, err
}
return cc, nil
}
// fake upd connect over tcp
type socks5UDPTunnelConn struct {
type fakeUDPTunnelConn struct {
// tcp connection
net.Conn
targetAddr net.Addr
}
func newSocks5UDPTunnelConn(conn net.Conn, serverAddr, targetAddr net.Addr) (net.Conn, error) {
cc, err := socks5Handshake(conn)
if err != nil {
return nil, err
}
req := gosocks5.NewRequest(gosocks5.CmdUdp, toSocksAddr(serverAddr))
if err := req.Write(cc); err != nil {
return nil, err
}
if util.Debug {
log.Debug("[socks5] udp-tun", req)
}
reply, err := gosocks5.ReadReply(cc)
if err != nil {
return nil, err
}
if util.Debug {
log.Debug("[socks5] udp-tun", reply)
}
if reply.Rep != gosocks5.Succeeded {
return nil, errors.New("socks5 UDP tunnel failure")
}
bindAddr, err := net.ResolveUDPAddr("udp", reply.Addr.String())
if err != nil {
return nil, err
}
log.Debugf("[socks5] udp-tun associate on %s OK", bindAddr)
return &socks5UDPTunnelConn{
Conn: cc,
func newFakeUDPTunnelConnOverTcp(conn net.Conn, targetAddr net.Addr) (net.Conn, error) {
return &fakeUDPTunnelConn{
Conn: conn,
targetAddr: targetAddr,
}, nil
}
func (c *socks5UDPTunnelConn) Read(b []byte) (n int, err error) {
func (c *fakeUDPTunnelConn) Read(b []byte) (n int, err error) {
n, _, err = c.ReadFrom(b)
return
}
func (c *socks5UDPTunnelConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
func (c *fakeUDPTunnelConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
dgram, err := gosocks5.ReadUDPDatagram(c.Conn)
if err != nil {
return
@@ -468,11 +311,11 @@ func (c *socks5UDPTunnelConn) ReadFrom(b []byte) (n int, addr net.Addr, err erro
return
}
func (c *socks5UDPTunnelConn) Write(b []byte) (n int, err error) {
func (c *fakeUDPTunnelConn) Write(b []byte) (n int, err error) {
return c.WriteTo(b, c.targetAddr)
}
func (c *socks5UDPTunnelConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
func (c *fakeUDPTunnelConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(len(b)), 0, toSocksAddr(addr)), b)
if err = dgram.Write(c.Conn); err != nil {
return

View File

@@ -17,10 +17,8 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"net"
"path/filepath"
"strings"
"testing"
"time"
@@ -28,10 +26,7 @@ import (
func TestCreateServer(t *testing.T) {
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{
ExplicitPath: clientcmd.RecommendedHomeFile,
},
nil,
&clientcmd.ClientConfigLoadingRules{ExplicitPath: clientcmd.RecommendedHomeFile}, nil,
)
config, err := clientConfig.ClientConfig()
if err != nil {
@@ -66,10 +61,7 @@ func TestGetIp(t *testing.T) {
func TestGetIPFromDHCP(t *testing.T) {
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{
ExplicitPath: filepath.Join(homedir.HomeDir(), clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName),
},
nil,
&clientcmd.ClientConfigLoadingRules{ExplicitPath: clientcmd.RecommendedHomeFile}, nil,
)
config, err := clientConfig.ClientConfig()
if err != nil {
@@ -80,13 +72,10 @@ func TestGetIPFromDHCP(t *testing.T) {
log.Fatal(err)
}
err = InitDHCP(clientset, "test", nil)
if err != nil {
fmt.Println(err)
}
manager := NewDHCPManager(clientset, "test", nil)
for i := 0; i < 10; i++ {
ipNet, err := GetIpFromDHCP(clientset, "test")
ipNet2, err := GetIpFromDHCP(clientset, "test")
ipNet, err := manager.RentIPRandom()
ipNet2, err := manager.RentIPRandom()
if err != nil {
fmt.Println(err)
continue
@@ -94,8 +83,8 @@ func TestGetIPFromDHCP(t *testing.T) {
fmt.Printf("%s->%s\n", ipNet.String(), ipNet2.String())
}
time.Sleep(time.Millisecond * 10)
err = ReleaseIpToDHCP(clientset, "test", ipNet)
err = ReleaseIpToDHCP(clientset, "test", ipNet2)
err = manager.ReleaseIpToDHCP(ipNet)
err = manager.ReleaseIpToDHCP(ipNet2)
if err != nil {
fmt.Println(err)
}