mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 11:15:07 +03:00 
			
		
		
		
	Add new reject channel to router so we can send back rejected packets to adapter (e.g. for ICMPv6 Packet Too Big), implement ICMPv6 PTB in TUN/TAP instead of router
This commit is contained in:
		
							parent
							
								
									0715e829c2
								
							
						
					
					
						commit
						eb22ed44ac
					
				
					 3 changed files with 135 additions and 96 deletions
				
			
		| 
						 | 
				
			
			@ -11,6 +11,8 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gologme/log"
 | 
			
		||||
	"golang.org/x/net/icmp"
 | 
			
		||||
	"golang.org/x/net/ipv6"
 | 
			
		||||
 | 
			
		||||
	"github.com/songgao/packets/ethernet"
 | 
			
		||||
	"github.com/yggdrasil-network/water"
 | 
			
		||||
| 
						 | 
				
			
			@ -64,10 +66,10 @@ func (tun *TunAdapter) IsTAP() bool {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Initialises the TUN/TAP adapter.
 | 
			
		||||
func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte) {
 | 
			
		||||
func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte, reject <-chan yggdrasil.RejectedPacket) {
 | 
			
		||||
	tun.config = config
 | 
			
		||||
	tun.log = log
 | 
			
		||||
	tun.Adapter.Init(config, log, send, recv)
 | 
			
		||||
	tun.Adapter.Init(config, log, send, recv, reject)
 | 
			
		||||
	tun.icmpv6.Init(tun)
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,81 +148,118 @@ func (tun *TunAdapter) Start(a address.Address, s address.Subnet) error {
 | 
			
		|||
// host operating system.
 | 
			
		||||
func (tun *TunAdapter) Write() error {
 | 
			
		||||
	for {
 | 
			
		||||
		data := <-tun.Recv
 | 
			
		||||
		if tun.iface == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if tun.iface.IsTAP() {
 | 
			
		||||
			var destAddr address.Address
 | 
			
		||||
			if data[0]&0xf0 == 0x60 {
 | 
			
		||||
				if len(data) < 40 {
 | 
			
		||||
					//panic("Tried to send a packet shorter than an IPv6 header...")
 | 
			
		||||
					util.PutBytes(data)
 | 
			
		||||
					continue
 | 
			
		||||
		select {
 | 
			
		||||
		case reject := <-tun.Reject:
 | 
			
		||||
			switch reject.Reason {
 | 
			
		||||
			case yggdrasil.PacketTooBig:
 | 
			
		||||
				if mtu, ok := reject.Detail.(int); ok {
 | 
			
		||||
					// Create the Packet Too Big response
 | 
			
		||||
					ptb := &icmp.PacketTooBig{
 | 
			
		||||
						MTU:  int(mtu),
 | 
			
		||||
						Data: reject.Packet,
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					// Create the ICMPv6 response from it
 | 
			
		||||
					icmpv6Buf, err := CreateICMPv6(
 | 
			
		||||
						reject.Packet[8:24], reject.Packet[24:40],
 | 
			
		||||
						ipv6.ICMPTypePacketTooBig, 0, ptb)
 | 
			
		||||
 | 
			
		||||
					// Send the ICMPv6 response back to the TUN/TAP adapter
 | 
			
		||||
					if err == nil {
 | 
			
		||||
						tun.iface.Write(icmpv6Buf)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				copy(destAddr[:16], data[24:])
 | 
			
		||||
			} else if data[0]&0xf0 == 0x40 {
 | 
			
		||||
				if len(data) < 20 {
 | 
			
		||||
					//panic("Tried to send a packet shorter than an IPv4 header...")
 | 
			
		||||
					util.PutBytes(data)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				copy(destAddr[:4], data[16:])
 | 
			
		||||
			} else {
 | 
			
		||||
				return errors.New("Invalid address family")
 | 
			
		||||
				fallthrough
 | 
			
		||||
			default:
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			sendndp := func(destAddr address.Address) {
 | 
			
		||||
				neigh, known := tun.icmpv6.peermacs[destAddr]
 | 
			
		||||
				known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30)
 | 
			
		||||
				if !known {
 | 
			
		||||
					request, err := tun.icmpv6.CreateNDPL2(destAddr)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						panic(err)
 | 
			
		||||
		case data := <-tun.Recv:
 | 
			
		||||
			if tun.iface == nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if tun.iface.IsTAP() {
 | 
			
		||||
				var destAddr address.Address
 | 
			
		||||
				if data[0]&0xf0 == 0x60 {
 | 
			
		||||
					if len(data) < 40 {
 | 
			
		||||
						//panic("Tried to send a packet shorter than an IPv6 header...")
 | 
			
		||||
						util.PutBytes(data)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					if _, err := tun.iface.Write(request); err != nil {
 | 
			
		||||
						panic(err)
 | 
			
		||||
					copy(destAddr[:16], data[24:])
 | 
			
		||||
				} else if data[0]&0xf0 == 0x40 {
 | 
			
		||||
					if len(data) < 20 {
 | 
			
		||||
						//panic("Tried to send a packet shorter than an IPv4 header...")
 | 
			
		||||
						util.PutBytes(data)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					tun.icmpv6.peermacs[destAddr] = neighbor{
 | 
			
		||||
						lastsolicitation: time.Now(),
 | 
			
		||||
					copy(destAddr[:4], data[16:])
 | 
			
		||||
				} else {
 | 
			
		||||
					return errors.New("Invalid address family")
 | 
			
		||||
				}
 | 
			
		||||
				sendndp := func(destAddr address.Address) {
 | 
			
		||||
					neigh, known := tun.icmpv6.peermacs[destAddr]
 | 
			
		||||
					known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30)
 | 
			
		||||
					if !known {
 | 
			
		||||
						request, err := tun.icmpv6.CreateNDPL2(destAddr)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							panic(err)
 | 
			
		||||
						}
 | 
			
		||||
						if _, err := tun.iface.Write(request); err != nil {
 | 
			
		||||
							panic(err)
 | 
			
		||||
						}
 | 
			
		||||
						tun.icmpv6.peermacs[destAddr] = neighbor{
 | 
			
		||||
							lastsolicitation: time.Now(),
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			var peermac macAddress
 | 
			
		||||
			var peerknown bool
 | 
			
		||||
			if data[0]&0xf0 == 0x40 {
 | 
			
		||||
				destAddr = tun.addr
 | 
			
		||||
			} else if data[0]&0xf0 == 0x60 {
 | 
			
		||||
				if !bytes.Equal(tun.addr[:16], destAddr[:16]) && !bytes.Equal(tun.subnet[:8], destAddr[:8]) {
 | 
			
		||||
				var peermac macAddress
 | 
			
		||||
				var peerknown bool
 | 
			
		||||
				if data[0]&0xf0 == 0x40 {
 | 
			
		||||
					destAddr = tun.addr
 | 
			
		||||
				} else if data[0]&0xf0 == 0x60 {
 | 
			
		||||
					if !bytes.Equal(tun.addr[:16], destAddr[:16]) && !bytes.Equal(tun.subnet[:8], destAddr[:8]) {
 | 
			
		||||
						destAddr = tun.addr
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if neighbor, ok := tun.icmpv6.peermacs[destAddr]; ok && neighbor.learned {
 | 
			
		||||
					peermac = neighbor.mac
 | 
			
		||||
					peerknown = true
 | 
			
		||||
				} else if neighbor, ok := tun.icmpv6.peermacs[tun.addr]; ok && neighbor.learned {
 | 
			
		||||
					peermac = neighbor.mac
 | 
			
		||||
					peerknown = true
 | 
			
		||||
					sendndp(destAddr)
 | 
			
		||||
				} else {
 | 
			
		||||
					sendndp(tun.addr)
 | 
			
		||||
				}
 | 
			
		||||
				if peerknown {
 | 
			
		||||
					var proto ethernet.Ethertype
 | 
			
		||||
					switch {
 | 
			
		||||
					case data[0]&0xf0 == 0x60:
 | 
			
		||||
						proto = ethernet.IPv6
 | 
			
		||||
					case data[0]&0xf0 == 0x40:
 | 
			
		||||
						proto = ethernet.IPv4
 | 
			
		||||
					}
 | 
			
		||||
					var frame ethernet.Frame
 | 
			
		||||
					frame.Prepare(
 | 
			
		||||
						peermac[:6],          // Destination MAC address
 | 
			
		||||
						tun.icmpv6.mymac[:6], // Source MAC address
 | 
			
		||||
						ethernet.NotTagged,   // VLAN tagging
 | 
			
		||||
						proto,                // Ethertype
 | 
			
		||||
						len(data))            // Payload length
 | 
			
		||||
					copy(frame[tun_ETHER_HEADER_LENGTH:], data[:])
 | 
			
		||||
					if _, err := tun.iface.Write(frame); err != nil {
 | 
			
		||||
						tun.mutex.RLock()
 | 
			
		||||
						open := tun.isOpen
 | 
			
		||||
						tun.mutex.RUnlock()
 | 
			
		||||
						if !open {
 | 
			
		||||
							return nil
 | 
			
		||||
						} else {
 | 
			
		||||
							panic(err)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if neighbor, ok := tun.icmpv6.peermacs[destAddr]; ok && neighbor.learned {
 | 
			
		||||
				peermac = neighbor.mac
 | 
			
		||||
				peerknown = true
 | 
			
		||||
			} else if neighbor, ok := tun.icmpv6.peermacs[tun.addr]; ok && neighbor.learned {
 | 
			
		||||
				peermac = neighbor.mac
 | 
			
		||||
				peerknown = true
 | 
			
		||||
				sendndp(destAddr)
 | 
			
		||||
			} else {
 | 
			
		||||
				sendndp(tun.addr)
 | 
			
		||||
			}
 | 
			
		||||
			if peerknown {
 | 
			
		||||
				var proto ethernet.Ethertype
 | 
			
		||||
				switch {
 | 
			
		||||
				case data[0]&0xf0 == 0x60:
 | 
			
		||||
					proto = ethernet.IPv6
 | 
			
		||||
				case data[0]&0xf0 == 0x40:
 | 
			
		||||
					proto = ethernet.IPv4
 | 
			
		||||
				}
 | 
			
		||||
				var frame ethernet.Frame
 | 
			
		||||
				frame.Prepare(
 | 
			
		||||
					peermac[:6],          // Destination MAC address
 | 
			
		||||
					tun.icmpv6.mymac[:6], // Source MAC address
 | 
			
		||||
					ethernet.NotTagged,   // VLAN tagging
 | 
			
		||||
					proto,                // Ethertype
 | 
			
		||||
					len(data))            // Payload length
 | 
			
		||||
				copy(frame[tun_ETHER_HEADER_LENGTH:], data[:])
 | 
			
		||||
				if _, err := tun.iface.Write(frame); err != nil {
 | 
			
		||||
				if _, err := tun.iface.Write(data); err != nil {
 | 
			
		||||
					tun.mutex.RLock()
 | 
			
		||||
					open := tun.isOpen
 | 
			
		||||
					tun.mutex.RUnlock()
 | 
			
		||||
| 
						 | 
				
			
			@ -231,19 +270,8 @@ func (tun *TunAdapter) Write() error {
 | 
			
		|||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if _, err := tun.iface.Write(data); err != nil {
 | 
			
		||||
				tun.mutex.RLock()
 | 
			
		||||
				open := tun.isOpen
 | 
			
		||||
				tun.mutex.RUnlock()
 | 
			
		||||
				if !open {
 | 
			
		||||
					return nil
 | 
			
		||||
				} else {
 | 
			
		||||
					panic(err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			util.PutBytes(data)
 | 
			
		||||
		}
 | 
			
		||||
		util.PutBytes(data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,12 +13,13 @@ type Adapter struct {
 | 
			
		|||
	Core        *Core
 | 
			
		||||
	Send        chan<- []byte
 | 
			
		||||
	Recv        <-chan []byte
 | 
			
		||||
	Reject      <-chan RejectedPacket
 | 
			
		||||
	Reconfigure chan chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Defines the minimum required functions for an adapter type
 | 
			
		||||
type adapterImplementation interface {
 | 
			
		||||
	Init(*config.NodeState, *log.Logger, chan<- []byte, <-chan []byte)
 | 
			
		||||
	Init(*config.NodeState, *log.Logger, chan<- []byte, <-chan []byte, <-chan RejectedPacket)
 | 
			
		||||
	Name() string
 | 
			
		||||
	MTU() int
 | 
			
		||||
	IsTAP() bool
 | 
			
		||||
| 
						 | 
				
			
			@ -29,9 +30,10 @@ type adapterImplementation interface {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Initialises the adapter.
 | 
			
		||||
func (adapter *Adapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte) {
 | 
			
		||||
func (adapter *Adapter) Init(config *config.NodeState, log *log.Logger, send chan<- []byte, recv <-chan []byte, reject <-chan RejectedPacket) {
 | 
			
		||||
	log.Traceln("Adapter setup - given channels:", send, recv)
 | 
			
		||||
	adapter.Send = send
 | 
			
		||||
	adapter.Recv = recv
 | 
			
		||||
	adapter.Reject = reject
 | 
			
		||||
	adapter.Reconfigure = make(chan chan error, 1)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,7 @@ type router struct {
 | 
			
		|||
	tun         adapterImplementation  // TUN/TAP adapter
 | 
			
		||||
	recv        chan<- []byte          // place where the tun pulls received packets from
 | 
			
		||||
	send        <-chan []byte          // place where the tun puts outgoing packets
 | 
			
		||||
	reject      chan<- RejectedPacket  // place where we send error packets back to tun
 | 
			
		||||
	reset       chan struct{}          // signal that coords changed (re-init sessions/dht)
 | 
			
		||||
	admin       chan func()            // pass a lambda for the admin socket to query stuff
 | 
			
		||||
	cryptokey   cryptokey
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +57,19 @@ type router_recvPacket struct {
 | 
			
		|||
	sinfo *sessionInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RejectedPacketReason int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// The router rejected the packet because it is too big for the session
 | 
			
		||||
	PacketTooBig = 1 + iota
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type RejectedPacket struct {
 | 
			
		||||
	Reason RejectedPacketReason
 | 
			
		||||
	Packet []byte
 | 
			
		||||
	Detail interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initializes the router struct, which includes setting up channels to/from the tun/tap.
 | 
			
		||||
func (r *router) init(core *Core) {
 | 
			
		||||
	r.core = core
 | 
			
		||||
| 
						 | 
				
			
			@ -103,8 +117,10 @@ func (r *router) init(core *Core) {
 | 
			
		|||
	r.toRecv = make(chan router_recvPacket, 32)
 | 
			
		||||
	recv := make(chan []byte, 32)
 | 
			
		||||
	send := make(chan []byte, 32)
 | 
			
		||||
	reject := make(chan RejectedPacket, 32)
 | 
			
		||||
	r.recv = recv
 | 
			
		||||
	r.send = send
 | 
			
		||||
	r.reject = reject
 | 
			
		||||
	r.reset = make(chan struct{}, 1)
 | 
			
		||||
	r.admin = make(chan func(), 32)
 | 
			
		||||
	r.nodeinfo.init(r.core)
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +128,7 @@ func (r *router) init(core *Core) {
 | 
			
		|||
	r.nodeinfo.setNodeInfo(r.core.config.Current.NodeInfo, r.core.config.Current.NodeInfoPrivacy)
 | 
			
		||||
	r.core.config.Mutex.RUnlock()
 | 
			
		||||
	r.cryptokey.init(r.core)
 | 
			
		||||
	r.tun.Init(&r.core.config, r.core.log, send, recv)
 | 
			
		||||
	r.tun.Init(&r.core.config, r.core.log, send, recv, reject)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Starts the mainLoop goroutine.
 | 
			
		||||
| 
						 | 
				
			
			@ -303,25 +319,18 @@ func (r *router) sendPacket(bs []byte) {
 | 
			
		|||
		// Generate an ICMPv6 Packet Too Big for packets larger than session MTU
 | 
			
		||||
		if len(bs) > int(sinfo.getMTU()) {
 | 
			
		||||
			// Get the size of the oversized payload, up to a max of 900 bytes
 | 
			
		||||
			/*window := 900
 | 
			
		||||
			window := 900
 | 
			
		||||
			if int(sinfo.getMTU()) < window {
 | 
			
		||||
				window = int(sinfo.getMTU())
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Create the Packet Too Big response
 | 
			
		||||
			ptb := &icmp.PacketTooBig{
 | 
			
		||||
				MTU:  int(sinfo.getMTU()),
 | 
			
		||||
				Data: bs[:window],
 | 
			
		||||
			// Send the error back to the adapter
 | 
			
		||||
			r.reject <- RejectedPacket{
 | 
			
		||||
				Reason: PacketTooBig,
 | 
			
		||||
				Packet: bs[:window],
 | 
			
		||||
				Detail: int(sinfo.getMTU()),
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Create the ICMPv6 response from it
 | 
			
		||||
			icmpv6Buf, err := CreateICMPv6(
 | 
			
		||||
				bs[8:24], bs[24:40],
 | 
			
		||||
				ipv6.ICMPTypePacketTooBig, 0, ptb)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				r.recv <- icmpv6Buf
 | 
			
		||||
			}*/
 | 
			
		||||
 | 
			
		||||
			// Don't continue - drop the packet
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue