5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-30 05:31:35 +00:00
yggdrasil-go/src/tuntap/conn.go

180 lines
4.3 KiB
Go
Raw Normal View History

package tuntap
import (
"bytes"
"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() (err error) {
select {
case _, ok := <-s.stop:
if !ok {
return errors.New("session was already closed")
}
default:
}
s.tun.log.Debugln("Starting conn reader for", s.conn.String())
defer s.tun.log.Debugln("Stopping conn reader for", s.conn.String())
2019-07-17 09:12:10 +00:00
for {
select {
case <-s.stop:
return nil
default:
}
var bs []byte
if bs, err = s.conn.ReadNoCopy(); err != nil {
if e, eok := err.(yggdrasil.ConnError); eok && !e.Temporary() {
if e.Closed() {
s.tun.log.Debugln(s.conn.String(), "TUN/TAP conn read debug:", err)
} else {
s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn read error:", err)
}
return e
}
} else if len(bs) > 0 {
if bs[0]&0xf0 == 0x60 {
switch {
case bs[8] == 0x02 && !bytes.Equal(s.addr[:16], bs[8:24]): // source
case bs[8] == 0x03 && !bytes.Equal(s.snet[:8], bs[8:16]): // source
case bs[24] == 0x02 && !bytes.Equal(s.tun.addr[:16], bs[24:40]): // destination
case bs[24] == 0x03 && !bytes.Equal(s.tun.subnet[:8], bs[24:32]): // destination
util.PutBytes(bs)
continue
default:
}
}
s.tun.send <- bs
s.stillAlive()
} else {
util.PutBytes(bs)
}
}
}
func (s *tunConn) writer() error {
select {
case _, ok := <-s.stop:
if !ok {
return errors.New("session was already closed")
}
default:
}
s.tun.log.Debugln("Starting conn writer for", s.conn.String())
defer s.tun.log.Debugln("Stopping conn writer for", s.conn.String())
for {
select {
case <-s.stop:
return nil
case bs, ok := <-s.send:
if !ok {
return errors.New("send closed")
}
if bs[0]&0xf0 == 0x60 {
switch {
case bs[8] == 0x02 && !bytes.Equal(s.tun.addr[:16], bs[8:24]): // source
case bs[8] == 0x03 && !bytes.Equal(s.tun.subnet[:8], bs[8:16]): // source
case bs[24] == 0x02 && !bytes.Equal(s.addr[:16], bs[24:40]): // destination
case bs[24] == 0x03 && !bytes.Equal(s.snet[:8], bs[24:32]): // destination
continue
default:
}
}
msg := yggdrasil.FlowKeyMessage{
FlowKey: util.GetFlowKey(bs),
Message: bs,
}
if err := s.conn.WriteNoCopy(msg); err != nil {
if e, eok := err.(yggdrasil.ConnError); !eok {
if e.Closed() {
s.tun.log.Debugln(s.conn.String(), "TUN/TAP generic write debug:", err)
} else {
s.tun.log.Errorln(s.conn.String(), "TUN/TAP generic write error:", err)
}
} else if e.PacketTooBig() {
2019-05-29 19:16:17 +00:00
// TODO: This currently isn't aware of IPv4 for CKR
ptb := &icmp.PacketTooBig{
MTU: int(e.PacketMaximumSize()),
Data: bs[:900],
2019-05-29 19:16:17 +00:00
}
if packet, err := CreateICMPv6(bs[8:24], bs[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil {
2019-05-29 19:16:17 +00:00
s.tun.send <- packet
}
} else {
if e.Closed() {
s.tun.log.Debugln(s.conn.String(), "TUN/TAP conn write debug:", err)
} else {
s.tun.log.Errorln(s.conn.String(), "TUN/TAP conn write error:", err)
}
2019-05-29 19:16:17 +00:00
}
} else {
s.stillAlive()
}
}
}
}
func (s *tunConn) stillAlive() {
2019-07-17 23:02:16 +00:00
defer func() { recover() }()
select {
case s.alive <- struct{}{}:
default:
}
}
func (s *tunConn) checkForTimeouts() error {
timer := time.NewTimer(tunConnTimeout)
defer util.TimerStop(timer)
defer s.close()
for {
select {
case _, ok := <-s.alive:
if !ok {
return errors.New("connection closed")
}
util.TimerStop(timer)
timer.Reset(tunConnTimeout)
case <-timer.C:
return errors.New("timed out")
}
}
}