1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
package l2tp
import (
"fmt"
"os"
"syscall"
"golang.org/x/sys/unix"
)
type controlPlane struct {
local, remote unix.Sockaddr
fd int
file *os.File
rc syscall.RawConn
connected bool
}
func (cp *controlPlane) recvFrom(p []byte) (n int, addr unix.Sockaddr, err error) {
cerr := cp.rc.Read(func(fd uintptr) bool {
n, addr, err = unix.Recvfrom(int(fd), p, unix.MSG_NOSIGNAL)
return err != unix.EAGAIN && err != unix.EWOULDBLOCK
})
if err != nil {
return n, addr, err
}
return n, addr, cerr
}
func (cp *controlPlane) write(b []byte) (n int, err error) {
if cp.connected {
return cp.file.Write(b)
}
return cp.writeTo(b, cp.remote)
}
func (cp *controlPlane) writeTo(p []byte, addr unix.Sockaddr) (n int, err error) {
return len(p), cp.sendto(p, addr)
}
func (cp *controlPlane) sendto(p []byte, to unix.Sockaddr) (err error) {
cerr := cp.rc.Write(func(fd uintptr) bool {
err = unix.Sendto(int(fd), p, unix.MSG_NOSIGNAL, to)
return err != unix.EAGAIN && err != unix.EWOULDBLOCK
})
if err != nil {
return err
}
return cerr
}
func (cp *controlPlane) close() (err error) {
if cp.file != nil {
err = cp.file.Close()
cp.file = nil
}
return
}
func (cp *controlPlane) connect() error {
err := unix.Connect(cp.fd, cp.remote)
if err == nil {
cp.connected = true
}
return err
}
func (cp *controlPlane) connectTo(sa unix.Sockaddr) error {
cp.remote = sa
return cp.connect()
}
func (cp *controlPlane) bind() error {
return unix.Bind(cp.fd, cp.local)
}
func tunnelSocket(family, protocol int) (fd int, err error) {
fd, err = unix.Socket(family, unix.SOCK_DGRAM, protocol)
if err != nil {
return -1, fmt.Errorf("socket: %v", err)
}
if err = unix.SetNonblock(fd, true); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("failed to set socket nonblocking: %v", err)
}
flags, err := unix.FcntlInt(uintptr(fd), unix.F_GETFD, 0)
if err != nil {
unix.Close(fd)
return -1, fmt.Errorf("fcntl(F_GETFD): %v", err)
}
_, err = unix.FcntlInt(uintptr(fd), unix.F_SETFD, flags|unix.FD_CLOEXEC)
if err != nil {
unix.Close(fd)
return -1, fmt.Errorf("fcntl(F_SETFD, FD_CLOEXEC): %v", err)
}
return fd, nil
}
func newL2tpControlPlane(localAddr, remoteAddr unix.Sockaddr) (*controlPlane, error) {
var family, protocol int
switch localAddr.(type) {
case *unix.SockaddrInet4:
family = unix.AF_INET
protocol = unix.IPPROTO_UDP
case *unix.SockaddrInet6:
family = unix.AF_INET6
protocol = unix.IPPROTO_UDP
case *unix.SockaddrL2TPIP:
family = unix.AF_INET
protocol = unix.IPPROTO_L2TP
case *unix.SockaddrL2TPIP6:
family = unix.AF_INET6
protocol = unix.IPPROTO_L2TP
default:
return nil, fmt.Errorf("unexpected address type %T", localAddr)
}
fd, err := tunnelSocket(family, protocol)
if err != nil {
return nil, err
}
file := os.NewFile(uintptr(fd), "l2tp")
sc, err := file.SyscallConn()
if err != nil {
unix.Close(fd)
return nil, err
}
return &controlPlane{
local: localAddr,
remote: remoteAddr,
fd: fd,
file: file,
rc: sc,
connected: false,
}, nil
}
|