summaryrefslogtreecommitdiff
path: root/pkg/server/socket/tun.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/server/socket/tun.go')
-rw-r--r--pkg/server/socket/tun.go133
1 files changed, 133 insertions, 0 deletions
diff --git a/pkg/server/socket/tun.go b/pkg/server/socket/tun.go
new file mode 100644
index 0000000..78bdfd4
--- /dev/null
+++ b/pkg/server/socket/tun.go
@@ -0,0 +1,133 @@
+package socket
+
+import (
+ "errors"
+ "fmt"
+ "golang.org/x/sys/unix"
+ "log"
+ "os"
+ "strings"
+ "sync"
+ "tunnel/pkg/pack"
+ "tunnel/pkg/server/env"
+ "tunnel/pkg/server/queue"
+ "unsafe"
+)
+
+const maxTunBufSize = 65535
+
+var errPartialWrite = errors.New("partial write")
+
+type ifReq struct {
+ name [unix.IFNAMSIZ]uint8
+ flags uint16
+}
+
+type tunSocket struct {
+ name string
+}
+
+type tunChannel struct {
+ name string
+ s *tunSocket
+ fp *os.File
+ once sync.Once
+}
+
+func ioctl(fd int, req uintptr, ptr unsafe.Pointer) error {
+ _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), req, uintptr(ptr))
+ if errno != 0 {
+ return errno
+ }
+
+ return nil
+}
+
+func newTunSocket(name string) (S, error) {
+ return &tunSocket{name: name}, nil
+
+}
+
+func (s *tunSocket) String() string {
+ return fmt.Sprintf("tun/%s", s.name)
+}
+
+func (s *tunSocket) Open(env.Env) (Channel, error) {
+ fd, err := unix.Open("/dev/net/tun", unix.O_RDWR, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ ifr := &ifReq{}
+ copy(ifr.name[:], s.name)
+ ifr.flags = unix.IFF_TUN | unix.IFF_NO_PI
+
+ if err := ioctl(fd, unix.TUNSETIFF, unsafe.Pointer(ifr)); err != nil {
+ unix.Close(fd)
+ return nil, fmt.Errorf("ioctl TUNSETIFF %s: %w", s.name, err)
+ }
+
+ if err := unix.SetNonblock(fd, true); err != nil {
+ unix.Close(fd)
+ return nil, fmt.Errorf("set nonblock %s: %w", s.name, err)
+ }
+
+ c := &tunChannel{
+ name: strings.Trim(string(ifr.name[:]), "\x00"),
+ fp: os.NewFile(uintptr(fd), "tun"),
+ }
+
+ return c, nil
+}
+
+func (s *tunSocket) Close() {
+}
+
+func (c *tunChannel) Send(wq queue.Q) error {
+ buf := make([]byte, maxTunBufSize)
+ enc := pack.NewEncoder(wq.Writer())
+
+ for {
+ n, err := c.fp.Read(buf)
+ if err != nil {
+ return err
+ }
+
+ enc.Lps(buf[0:n])
+ }
+}
+
+func (c *tunChannel) Recv(rq queue.Q) error {
+ dec := pack.NewDecoder(rq.Reader())
+
+ for {
+ b, err := dec.Lps()
+ if err != nil {
+ return err
+ }
+
+ n, err := c.fp.Write(b)
+ if err != nil {
+ return err
+ }
+
+ if n != len(b) {
+ return errPartialWrite
+ }
+ }
+}
+
+func (c *tunChannel) String() string {
+ return "tun/" + c.name
+}
+
+func (c *tunChannel) Close() error {
+ err := ErrAlreadyClosed
+
+ c.once.Do(func() {
+ log.Println("close", c)
+ err = c.fp.Close()
+ })
+
+ return err
+}