5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-26 21:11:36 +00:00
yggdrasil-go/src/tuntap/conn.go

148 lines
3.5 KiB
Go
Raw Normal View History

package tuntap
import (
"errors"
"time"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
2019-05-29 19:16:17 +00:00
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
)
2019-07-17 09:12:10 +00:00
const tunConnTimeout = 2 * time.Minute
type tunConn struct {
tun *TunAdapter
conn *yggdrasil.Conn
addr address.Address
snet address.Subnet
send chan []byte
stop chan struct{}
alive chan struct{}
}
func (s *tunConn) close() {
s.tun.mutex.Lock()
defer s.tun.mutex.Unlock()
s._close_nomutex()
}
func (s *tunConn) _close_nomutex() {
s.conn.Close()
delete(s.tun.addrToConn, s.addr)
delete(s.tun.subnetToConn, s.snet)
func() {
defer func() { recover() }()
close(s.stop) // Closes reader/writer goroutines
}()
func() {
defer func() { recover() }()
close(s.alive) // Closes timeout goroutine
}()
}
func (s *tunConn) reader() error {
select {
case _, ok := <-s.stop:
if !ok {
return errors.New("session was already closed")
}
default:
}
2019-07-17 09:12:10 +00:00
s.tun.log.Debugln("Starting conn reader for", s)
2019-07-17 10:13:53 +00:00
defer s.tun.log.Debugln("Stopping conn reader for", s)
var n int
var err error
read := make(chan bool)
b := make([]byte, 65535)
2019-07-17 09:12:10 +00:00
go func() {
s.tun.log.Debugln("Starting conn reader helper for", s)
2019-07-17 10:13:53 +00:00
defer s.tun.log.Debugln("Stopping conn reader helper for", s)
2019-07-17 09:12:10 +00:00
for {
s.conn.SetReadDeadline(time.Now().Add(tunConnTimeout))
if n, err = s.conn.Read(b); err != nil {
s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn read error:", err)
if e, eok := err.(yggdrasil.ConnError); eok && !e.Temporary() {
2019-07-17 10:13:53 +00:00
s.tun.log.Debugln("Conn reader helper", s, "error:", e)
2019-07-17 09:12:10 +00:00
switch {
// The timeout probably means we've waited for the timeout period and
// nothing has happened so close the connection
2019-07-17 10:13:53 +00:00
case e.Timeout():
s.close()
2019-07-17 09:12:10 +00:00
continue
// The connection is already closed, so we anticipate that the main
// reader goroutine has already exited. Also stop in that case
2019-07-17 10:13:53 +00:00
case e.Closed():
return
// Some other case that we don't know about - close the connection
2019-07-17 09:12:10 +00:00
default:
s.close()
return
}
}
2019-07-17 09:12:10 +00:00
read <- false
}
read <- true
2019-07-17 09:12:10 +00:00
}
}()
for {
select {
case r := <-read:
if r && n > 0 {
bs := append(util.GetBytes(), b[:n]...)
select {
case s.tun.send <- bs:
default:
util.PutBytes(bs)
}
}
case <-s.stop:
return nil
}
}
}
func (s *tunConn) writer() error {
select {
case _, ok := <-s.stop:
if !ok {
return errors.New("session was already closed")
}
default:
}
2019-07-17 09:12:10 +00:00
s.tun.log.Debugln("Starting conn writer for", s)
2019-07-17 10:13:53 +00:00
defer s.tun.log.Debugln("Stopping conn writer for", s)
for {
select {
case <-s.stop:
return nil
case b, ok := <-s.send:
if !ok {
return errors.New("send closed")
}
// TODO write timeout and close
s.conn.SetWriteDeadline(time.Now().Add(tunConnTimeout))
if _, err := s.conn.Write(b); err != nil {
2019-05-29 19:16:17 +00:00
e, eok := err.(yggdrasil.ConnError)
if !eok {
s.tun.log.Errorln(s.conn.String(), "TUN/TAP generic write error:", err)
} else if ispackettoobig, maxsize := e.PacketTooBig(); ispackettoobig {
// TODO: This currently isn't aware of IPv4 for CKR
ptb := &icmp.PacketTooBig{
MTU: int(maxsize),
Data: b[:900],
}
if packet, err := CreateICMPv6(b[8:24], b[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil {
s.tun.send <- packet
}
} else {
s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn write error:", err)
}
}
util.PutBytes(b)
}
}
}