package socket import ( "errors" "fmt" "golang.org/x/sys/unix" "log" "os" "strings" "sync" "unsafe" "tunnel/pkg/pack" "tunnel/pkg/server/env" "tunnel/pkg/server/queue" ) const maxTunBufSize = 65535 var errPartialWrite = errors.New("partial write") type ifReq struct { name [unix.IFNAMSIZ]uint8 flags uint16 } type tunSocket struct { Name string `opts:"required"` } type tunConn struct { name string s *tunSocket fp *os.File once sync.Once } func (s *tunSocket) String() string { return fmt.Sprintf("tun/%s", s.Name) } func (s *tunSocket) Single() {} func (s *tunSocket) New(env.Env) (Conn, 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 := &tunConn{ name: strings.Trim(string(ifr.name[:]), "\x00"), fp: os.NewFile(uintptr(fd), "tun"), } return c, nil } func (s *tunSocket) Close() { } func (c *tunConn) 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 *tunConn) 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 *tunConn) String() string { return "tun/" + c.name } func (c *tunConn) Close() error { err := ErrAlreadyClosed c.once.Do(func() { log.Println("close", c) err = c.fp.Close() }) return err } func init() { register("tun", tunSocket{}) }