mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-27 12:38:11 +00:00
Merge pull request #109 from djs55/fix-udp-proxy
proxy: add support for UDP
This commit is contained in:
commit
da23023a92
@ -4,8 +4,8 @@ package libproxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"github.com/djs55/vsock"
|
"github.com/djs55/vsock"
|
||||||
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Proxy defines the behavior of a proxy. It forwards traffic back and forth
|
// Proxy defines the behavior of a proxy. It forwards traffic back and forth
|
||||||
@ -25,21 +25,17 @@ type Proxy interface {
|
|||||||
BackendAddr() net.Addr
|
BackendAddr() net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// NewProxy creates a Proxy according to the specified frontendAddr and backendAddr.
|
// NewProxy creates a Proxy according to the specified frontendAddr and backendAddr.
|
||||||
func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
|
func NewProxy(frontendAddr *vsock.VsockAddr, backendAddr net.Addr) (Proxy, error) {
|
||||||
switch frontendAddr.(type) {
|
switch backendAddr.(type) {
|
||||||
case *net.UDPAddr:
|
case *net.UDPAddr:
|
||||||
return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr))
|
listener, err := vsock.Listen(frontendAddr.Port)
|
||||||
case *net.TCPAddr:
|
|
||||||
listener, err := net.Listen("tcp", frontendAddr.String())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewTCPProxy(listener, backendAddr.(*net.TCPAddr))
|
return NewUDPProxy(frontendAddr, NewUDPListener(listener), backendAddr.(*net.UDPAddr))
|
||||||
case *vsock.VsockAddr:
|
case *net.TCPAddr:
|
||||||
listener, err := vsock.Listen(frontendAddr.(*vsock.VsockAddr).Port)
|
listener, err := vsock.Listen(frontendAddr.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
179
alpine/packages/proxy/libproxy/udp_encapsulation.go
Normal file
179
alpine/packages/proxy/libproxy/udp_encapsulation.go
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
package libproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type udpListener interface {
|
||||||
|
ReadFromUDP(b []byte) (int, *net.UDPAddr, error)
|
||||||
|
WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type udpEncapsulator struct {
|
||||||
|
conn *net.Conn
|
||||||
|
listener net.Listener
|
||||||
|
m *sync.Mutex
|
||||||
|
r *sync.Mutex
|
||||||
|
w *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpEncapsulator) getConn() (net.Conn, error) {
|
||||||
|
u.m.Lock()
|
||||||
|
defer u.m.Unlock()
|
||||||
|
if u.conn != nil {
|
||||||
|
return *u.conn, nil
|
||||||
|
}
|
||||||
|
conn, err := u.listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Printf("Failed to accept connection: %#v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u.conn = &conn
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpEncapsulator) ReadFromUDP(b []byte) (int, *net.UDPAddr, error) {
|
||||||
|
conn, err := u.getConn()
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
u.r.Lock()
|
||||||
|
defer u.r.Unlock()
|
||||||
|
datagram := &udpDatagram{payload: b}
|
||||||
|
length, err := datagram.Unmarshal(conn)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
udpAddr := net.UDPAddr{IP: *datagram.IP, Port: int(datagram.Port), Zone: datagram.Zone}
|
||||||
|
return length, &udpAddr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpEncapsulator) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
|
||||||
|
conn, err := u.getConn()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
u.w.Lock()
|
||||||
|
defer u.w.Unlock()
|
||||||
|
datagram := &udpDatagram{payload: b, IP: &addr.IP, Port: uint16(addr.Port), Zone: addr.Zone}
|
||||||
|
return len(b), datagram.Marshal(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpEncapsulator) Close() error {
|
||||||
|
if u.conn != nil {
|
||||||
|
conn := *u.conn
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
u.listener.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPListener(listener net.Listener) udpListener {
|
||||||
|
var m sync.Mutex
|
||||||
|
var r sync.Mutex
|
||||||
|
var w sync.Mutex
|
||||||
|
return &udpEncapsulator{
|
||||||
|
conn: nil,
|
||||||
|
listener: listener,
|
||||||
|
m: &m,
|
||||||
|
r: &r,
|
||||||
|
w: &w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type udpDatagram struct {
|
||||||
|
payload []byte
|
||||||
|
IP *net.IP
|
||||||
|
Port uint16
|
||||||
|
Zone string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpDatagram) Marshal(conn net.Conn) error {
|
||||||
|
// marshal the variable length header to a temporary buffer
|
||||||
|
var header bytes.Buffer
|
||||||
|
var length uint16
|
||||||
|
length = uint16(len(*u.IP))
|
||||||
|
if err := binary.Write(&header, binary.LittleEndian, &length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(&header, binary.LittleEndian, u.IP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(&header, binary.LittleEndian, &u.Port); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
length = uint16(len(u.Zone))
|
||||||
|
if err := binary.Write(&header, binary.LittleEndian, &length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(&header, binary.LittleEndian, []byte(u.Zone)); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
length = uint16(len(u.payload))
|
||||||
|
if err := binary.Write(&header, binary.LittleEndian, &length); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
length = uint16(2 + header.Len() + len(u.payload))
|
||||||
|
if err := binary.Write(conn, binary.LittleEndian, &length); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := io.Copy(conn, &header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
payload := bytes.NewBuffer(u.payload)
|
||||||
|
_, err = io.Copy(conn, payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *udpDatagram) Unmarshal(conn net.Conn) (int, error) {
|
||||||
|
var length uint16
|
||||||
|
// frame length
|
||||||
|
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var IP net.IP
|
||||||
|
IP = make([]byte, length)
|
||||||
|
if err := binary.Read(conn, binary.LittleEndian, &IP); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
u.IP = &IP
|
||||||
|
if err := binary.Read(conn, binary.LittleEndian, &u.Port); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
Zone := make([]byte, length)
|
||||||
|
if err := binary.Read(conn, binary.LittleEndian, &Zone); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
u.Zone = string(Zone)
|
||||||
|
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
_, err := io.ReadFull(conn, u.payload[0:length])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(length), nil
|
||||||
|
}
|
@ -47,22 +47,19 @@ type connTrackMap map[connTrackKey]*net.UDPConn
|
|||||||
// interface to handle UDP traffic forwarding between the frontend and backend
|
// interface to handle UDP traffic forwarding between the frontend and backend
|
||||||
// addresses.
|
// addresses.
|
||||||
type UDPProxy struct {
|
type UDPProxy struct {
|
||||||
listener *net.UDPConn
|
listener udpListener
|
||||||
frontendAddr *net.UDPAddr
|
frontendAddr net.Addr
|
||||||
backendAddr *net.UDPAddr
|
backendAddr *net.UDPAddr
|
||||||
connTrackTable connTrackMap
|
connTrackTable connTrackMap
|
||||||
connTrackLock sync.Mutex
|
connTrackLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUDPProxy creates a new UDPProxy.
|
// NewUDPProxy creates a new UDPProxy.
|
||||||
func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
|
func NewUDPProxy(frontendAddr net.Addr, listener udpListener, backendAddr *net.UDPAddr) (*UDPProxy, error) {
|
||||||
listener, err := net.ListenUDP("udp", frontendAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &UDPProxy{
|
return &UDPProxy{
|
||||||
listener: listener,
|
listener: listener,
|
||||||
frontendAddr: listener.LocalAddr().(*net.UDPAddr),
|
frontendAddr: frontendAddr,
|
||||||
backendAddr: backendAddr,
|
backendAddr: backendAddr,
|
||||||
connTrackTable: make(connTrackMap),
|
connTrackTable: make(connTrackMap),
|
||||||
}, nil
|
}, nil
|
||||||
@ -112,7 +109,7 @@ func (proxy *UDPProxy) Run() {
|
|||||||
// ECONNREFUSED like Read do (see comment in
|
// ECONNREFUSED like Read do (see comment in
|
||||||
// UDPProxy.replyLoop)
|
// UDPProxy.replyLoop)
|
||||||
if !isClosedError(err) {
|
if !isClosedError(err) {
|
||||||
logrus.Printf("Stopping proxy on udp/%v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
|
logrus.Printf("Stopping proxy on %v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -14,17 +14,17 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
host, port, container := parseHostContainerAddrs()
|
host, port, container := parseHostContainerAddrs()
|
||||||
|
|
||||||
|
p, err := libproxy.NewProxy(&vsock.VsockAddr{Port: uint(port)}, container)
|
||||||
|
if err != nil {
|
||||||
|
sendError(err)
|
||||||
|
}
|
||||||
ctl, err := exposePort(host, port)
|
ctl, err := exposePort(host, port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(err)
|
sendError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := libproxy.NewProxy(&vsock.VsockAddr{Port: uint(port)}, container)
|
|
||||||
if err != nil {
|
|
||||||
sendError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go handleStopSignals(p)
|
go handleStopSignals(p)
|
||||||
|
// TODO: avoid this line if we are running in a TTY
|
||||||
sendOK()
|
sendOK()
|
||||||
p.Run()
|
p.Run()
|
||||||
ctl.Close() // ensure ctl remains alive and un-GCed until here
|
ctl.Close() // ensure ctl remains alive and un-GCed until here
|
||||||
@ -32,7 +32,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exposePort(host net.Addr, port int) (*os.File, error) {
|
func exposePort(host net.Addr, port int) (*os.File, error) {
|
||||||
name := host.String()
|
name := host.Network() + ":" + host.String()
|
||||||
log.Printf("exposePort %s\n", name)
|
log.Printf("exposePort %s\n", name)
|
||||||
err := os.Mkdir("/port/"+name, 0)
|
err := os.Mkdir("/port/"+name, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -28,8 +28,11 @@ func sendOK() {
|
|||||||
f.Close()
|
f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map dynamic ports onto vsock ports over this offset
|
// Map dynamic TCP ports onto vsock ports over this offset
|
||||||
var vSockPortOffset = 0x10000
|
var vSockTCPPortOffset = 0x10000
|
||||||
|
|
||||||
|
// Map dynamic UDP ports onto vsock ports over this offset
|
||||||
|
var vSockUDPPortOffset = 0x20000
|
||||||
|
|
||||||
// From docker/libnetwork/portmapper/proxy.go:
|
// From docker/libnetwork/portmapper/proxy.go:
|
||||||
|
|
||||||
@ -49,11 +52,11 @@ func parseHostContainerAddrs() (host net.Addr, port int, container net.Addr) {
|
|||||||
switch *proto {
|
switch *proto {
|
||||||
case "tcp":
|
case "tcp":
|
||||||
host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
||||||
port = vSockPortOffset + *hostPort
|
port = vSockTCPPortOffset + *hostPort
|
||||||
container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
||||||
case "udp":
|
case "udp":
|
||||||
host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
||||||
port = vSockPortOffset + *hostPort
|
port = vSockUDPPortOffset + *hostPort
|
||||||
container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
||||||
default:
|
default:
|
||||||
log.Fatalf("unsupported protocol %s", *proto)
|
log.Fatalf("unsupported protocol %s", *proto)
|
||||||
|
Loading…
Reference in New Issue
Block a user