mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 03:05:07 +03:00 
			
		
		
		
	move some logic from TunAdapter.reader into a new function, TunAdapter.readerPacketHandler
This commit is contained in:
		
							parent
							
								
									38e1503b28
								
							
						
					
					
						commit
						406e143f7f
					
				
					 1 changed files with 178 additions and 173 deletions
				
			
		| 
						 | 
				
			
			@ -110,178 +110,10 @@ func (tun *TunAdapter) writer() error {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tun *TunAdapter) reader() error {
 | 
			
		||||
	recvd := make([]byte, 65535+tun_ETHER_HEADER_LENGTH)
 | 
			
		||||
	toWorker := make(chan []byte, 32)
 | 
			
		||||
	defer close(toWorker)
 | 
			
		||||
	go func() {
 | 
			
		||||
		for bs := range toWorker {
 | 
			
		||||
			// If we detect an ICMP packet then hand it to the ICMPv6 module
 | 
			
		||||
			if bs[6] == 58 {
 | 
			
		||||
				// Found an ICMPv6 packet - we need to make sure to give ICMPv6 the full
 | 
			
		||||
				// Ethernet frame rather than just the IPv6 packet as this is needed for
 | 
			
		||||
				// NDP to work correctly
 | 
			
		||||
				if err := tun.icmpv6.ParsePacket(recvd); err == nil {
 | 
			
		||||
					// We acted on the packet in the ICMPv6 module so don't forward or do
 | 
			
		||||
					// anything else with it
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			// From the IP header, work out what our source and destination addresses
 | 
			
		||||
			// and node IDs are. We will need these in order to work out where to send
 | 
			
		||||
			// the packet
 | 
			
		||||
			var srcAddr address.Address
 | 
			
		||||
			var dstAddr address.Address
 | 
			
		||||
			var dstNodeID *crypto.NodeID
 | 
			
		||||
			var dstNodeIDMask *crypto.NodeID
 | 
			
		||||
			var dstSnet address.Subnet
 | 
			
		||||
			var addrlen int
 | 
			
		||||
			n := len(bs)
 | 
			
		||||
			// Check the IP protocol - if it doesn't match then we drop the packet and
 | 
			
		||||
			// do nothing with it
 | 
			
		||||
			if bs[0]&0xf0 == 0x60 {
 | 
			
		||||
				// Check if we have a fully-sized IPv6 header
 | 
			
		||||
				if len(bs) < 40 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				// Check the packet size
 | 
			
		||||
				if n-tun_IPv6_HEADER_LENGTH != 256*int(bs[4])+int(bs[5]) {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				// IPv6 address
 | 
			
		||||
				addrlen = 16
 | 
			
		||||
				copy(srcAddr[:addrlen], bs[8:])
 | 
			
		||||
				copy(dstAddr[:addrlen], bs[24:])
 | 
			
		||||
				copy(dstSnet[:addrlen/2], bs[24:])
 | 
			
		||||
			} else if bs[0]&0xf0 == 0x40 {
 | 
			
		||||
				// Check if we have a fully-sized IPv4 header
 | 
			
		||||
				if len(bs) < 20 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				// Check the packet size
 | 
			
		||||
				if n != 256*int(bs[2])+int(bs[3]) {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				// IPv4 address
 | 
			
		||||
				addrlen = 4
 | 
			
		||||
				copy(srcAddr[:addrlen], bs[12:])
 | 
			
		||||
				copy(dstAddr[:addrlen], bs[16:])
 | 
			
		||||
			} else {
 | 
			
		||||
				// Unknown address length or protocol, so drop the packet and ignore it
 | 
			
		||||
				tun.log.Traceln("Unknown packet type, dropping")
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if tun.ckr.isEnabled() && !tun.ckr.isValidSource(srcAddr, addrlen) {
 | 
			
		||||
				// The packet had a source address that doesn't belong to us or our
 | 
			
		||||
				// configured crypto-key routing source subnets
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if !dstAddr.IsValid() && !dstSnet.IsValid() {
 | 
			
		||||
				if key, err := tun.ckr.getPublicKeyForAddress(dstAddr, addrlen); err == nil {
 | 
			
		||||
					// A public key was found, get the node ID for the search
 | 
			
		||||
					dstNodeID = crypto.GetNodeID(&key)
 | 
			
		||||
					// Do a quick check to ensure that the node ID refers to a vaild
 | 
			
		||||
					// Yggdrasil address or subnet - this might be superfluous
 | 
			
		||||
					addr := *address.AddrForNodeID(dstNodeID)
 | 
			
		||||
					copy(dstAddr[:], addr[:])
 | 
			
		||||
					copy(dstSnet[:], addr[:])
 | 
			
		||||
					// Are we certain we looked up a valid node?
 | 
			
		||||
					if !dstAddr.IsValid() && !dstSnet.IsValid() {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					// No public key was found in the CKR table so we've exhausted our options
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			// Do we have an active connection for this node address?
 | 
			
		||||
			tun.mutex.RLock()
 | 
			
		||||
			session, isIn := tun.addrToConn[dstAddr]
 | 
			
		||||
			if !isIn || session == nil {
 | 
			
		||||
				session, isIn = tun.subnetToConn[dstSnet]
 | 
			
		||||
				if !isIn || session == nil {
 | 
			
		||||
					// Neither an address nor a subnet mapping matched, therefore populate
 | 
			
		||||
					// the node ID and mask to commence a search
 | 
			
		||||
					if dstAddr.IsValid() {
 | 
			
		||||
						dstNodeID, dstNodeIDMask = dstAddr.GetNodeIDandMask()
 | 
			
		||||
					} else {
 | 
			
		||||
						dstNodeID, dstNodeIDMask = dstSnet.GetNodeIDandMask()
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			tun.mutex.RUnlock()
 | 
			
		||||
			// If we don't have a connection then we should open one
 | 
			
		||||
			if !isIn || session == nil {
 | 
			
		||||
				// Check we haven't been given empty node ID, really this shouldn't ever
 | 
			
		||||
				// happen but just to be sure...
 | 
			
		||||
				if dstNodeID == nil || dstNodeIDMask == nil {
 | 
			
		||||
					panic("Given empty dstNodeID and dstNodeIDMask - this shouldn't happen")
 | 
			
		||||
				}
 | 
			
		||||
				// Dial to the remote node
 | 
			
		||||
				packet := bs
 | 
			
		||||
				go func() {
 | 
			
		||||
					// FIXME just spitting out a goroutine to do this is kind of ugly and means we drop packets until the dial finishes
 | 
			
		||||
					tun.mutex.Lock()
 | 
			
		||||
					_, known := tun.dials[*dstNodeID]
 | 
			
		||||
					tun.dials[*dstNodeID] = append(tun.dials[*dstNodeID], packet)
 | 
			
		||||
					for len(tun.dials[*dstNodeID]) > 32 {
 | 
			
		||||
						util.PutBytes(tun.dials[*dstNodeID][0])
 | 
			
		||||
						tun.dials[*dstNodeID] = tun.dials[*dstNodeID][1:]
 | 
			
		||||
					}
 | 
			
		||||
					tun.mutex.Unlock()
 | 
			
		||||
					if known {
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
					var tc *tunConn
 | 
			
		||||
					if conn, err := tun.dialer.DialByNodeIDandMask(dstNodeID, dstNodeIDMask); err == nil {
 | 
			
		||||
						// We've been given a connection so prepare the session wrapper
 | 
			
		||||
						if tc, err = tun.wrap(conn); err != nil {
 | 
			
		||||
							// Something went wrong when storing the connection, typically that
 | 
			
		||||
							// something already exists for this address or subnet
 | 
			
		||||
							tun.log.Debugln("TUN/TAP iface wrap:", err)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					tun.mutex.Lock()
 | 
			
		||||
					packets := tun.dials[*dstNodeID]
 | 
			
		||||
					delete(tun.dials, *dstNodeID)
 | 
			
		||||
					tun.mutex.Unlock()
 | 
			
		||||
					if tc != nil {
 | 
			
		||||
						for _, packet := range packets {
 | 
			
		||||
							select {
 | 
			
		||||
							case tc.send <- packet:
 | 
			
		||||
							default:
 | 
			
		||||
								util.PutBytes(packet)
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}()
 | 
			
		||||
				// While the dial is going on we can't do much else
 | 
			
		||||
				// continuing this iteration - skip to the next one
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// If we have a connection now, try writing to it
 | 
			
		||||
			if isIn && session != nil {
 | 
			
		||||
				packet := bs
 | 
			
		||||
				select {
 | 
			
		||||
				case session.send <- packet:
 | 
			
		||||
				default:
 | 
			
		||||
					util.PutBytes(packet)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	for {
 | 
			
		||||
		// Wait for a packet to be delivered to us through the TUN/TAP adapter
 | 
			
		||||
		n, err := tun.iface.Read(recvd)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if !tun.isOpen {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		if n == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
// Run in a separate goroutine by the reader
 | 
			
		||||
// Does all of the per-packet ICMP checks, passes packets to the right Conn worker
 | 
			
		||||
func (tun *TunAdapter) readerPacketHandler(ch chan []byte) {
 | 
			
		||||
	for recvd := range ch {
 | 
			
		||||
		// If it's a TAP adapter, update the buffer slice so that we no longer
 | 
			
		||||
		// include the ethernet headers
 | 
			
		||||
		offset := 0
 | 
			
		||||
| 
						 | 
				
			
			@ -295,7 +127,180 @@ func (tun *TunAdapter) reader() error {
 | 
			
		|||
		}
 | 
			
		||||
		// Offset the buffer from now on so that we can ignore ethernet frames if
 | 
			
		||||
		// they are present
 | 
			
		||||
		bs := append(util.GetBytes(), recvd[offset:offset+n]...)
 | 
			
		||||
		bs := recvd[offset:]
 | 
			
		||||
		// If we detect an ICMP packet then hand it to the ICMPv6 module
 | 
			
		||||
		if bs[6] == 58 {
 | 
			
		||||
			// Found an ICMPv6 packet - we need to make sure to give ICMPv6 the full
 | 
			
		||||
			// Ethernet frame rather than just the IPv6 packet as this is needed for
 | 
			
		||||
			// NDP to work correctly
 | 
			
		||||
			if err := tun.icmpv6.ParsePacket(recvd); err == nil {
 | 
			
		||||
				// We acted on the packet in the ICMPv6 module so don't forward or do
 | 
			
		||||
				// anything else with it
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Shift forward to avoid leaking bytes off the front of the slide when we eventually store it
 | 
			
		||||
		bs = append(recvd[:0], bs...)
 | 
			
		||||
		// From the IP header, work out what our source and destination addresses
 | 
			
		||||
		// and node IDs are. We will need these in order to work out where to send
 | 
			
		||||
		// the packet
 | 
			
		||||
		var srcAddr address.Address
 | 
			
		||||
		var dstAddr address.Address
 | 
			
		||||
		var dstNodeID *crypto.NodeID
 | 
			
		||||
		var dstNodeIDMask *crypto.NodeID
 | 
			
		||||
		var dstSnet address.Subnet
 | 
			
		||||
		var addrlen int
 | 
			
		||||
		n := len(bs)
 | 
			
		||||
		// Check the IP protocol - if it doesn't match then we drop the packet and
 | 
			
		||||
		// do nothing with it
 | 
			
		||||
		if bs[0]&0xf0 == 0x60 {
 | 
			
		||||
			// Check if we have a fully-sized IPv6 header
 | 
			
		||||
			if len(bs) < 40 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// Check the packet size
 | 
			
		||||
			if n-tun_IPv6_HEADER_LENGTH != 256*int(bs[4])+int(bs[5]) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// IPv6 address
 | 
			
		||||
			addrlen = 16
 | 
			
		||||
			copy(srcAddr[:addrlen], bs[8:])
 | 
			
		||||
			copy(dstAddr[:addrlen], bs[24:])
 | 
			
		||||
			copy(dstSnet[:addrlen/2], bs[24:])
 | 
			
		||||
		} else if bs[0]&0xf0 == 0x40 {
 | 
			
		||||
			// Check if we have a fully-sized IPv4 header
 | 
			
		||||
			if len(bs) < 20 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// Check the packet size
 | 
			
		||||
			if n != 256*int(bs[2])+int(bs[3]) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// IPv4 address
 | 
			
		||||
			addrlen = 4
 | 
			
		||||
			copy(srcAddr[:addrlen], bs[12:])
 | 
			
		||||
			copy(dstAddr[:addrlen], bs[16:])
 | 
			
		||||
		} else {
 | 
			
		||||
			// Unknown address length or protocol, so drop the packet and ignore it
 | 
			
		||||
			tun.log.Traceln("Unknown packet type, dropping")
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if tun.ckr.isEnabled() && !tun.ckr.isValidSource(srcAddr, addrlen) {
 | 
			
		||||
			// The packet had a source address that doesn't belong to us or our
 | 
			
		||||
			// configured crypto-key routing source subnets
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if !dstAddr.IsValid() && !dstSnet.IsValid() {
 | 
			
		||||
			if key, err := tun.ckr.getPublicKeyForAddress(dstAddr, addrlen); err == nil {
 | 
			
		||||
				// A public key was found, get the node ID for the search
 | 
			
		||||
				dstNodeID = crypto.GetNodeID(&key)
 | 
			
		||||
				// Do a quick check to ensure that the node ID refers to a vaild
 | 
			
		||||
				// Yggdrasil address or subnet - this might be superfluous
 | 
			
		||||
				addr := *address.AddrForNodeID(dstNodeID)
 | 
			
		||||
				copy(dstAddr[:], addr[:])
 | 
			
		||||
				copy(dstSnet[:], addr[:])
 | 
			
		||||
				// Are we certain we looked up a valid node?
 | 
			
		||||
				if !dstAddr.IsValid() && !dstSnet.IsValid() {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				// No public key was found in the CKR table so we've exhausted our options
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Do we have an active connection for this node address?
 | 
			
		||||
		tun.mutex.RLock()
 | 
			
		||||
		session, isIn := tun.addrToConn[dstAddr]
 | 
			
		||||
		if !isIn || session == nil {
 | 
			
		||||
			session, isIn = tun.subnetToConn[dstSnet]
 | 
			
		||||
			if !isIn || session == nil {
 | 
			
		||||
				// Neither an address nor a subnet mapping matched, therefore populate
 | 
			
		||||
				// the node ID and mask to commence a search
 | 
			
		||||
				if dstAddr.IsValid() {
 | 
			
		||||
					dstNodeID, dstNodeIDMask = dstAddr.GetNodeIDandMask()
 | 
			
		||||
				} else {
 | 
			
		||||
					dstNodeID, dstNodeIDMask = dstSnet.GetNodeIDandMask()
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		tun.mutex.RUnlock()
 | 
			
		||||
		// If we don't have a connection then we should open one
 | 
			
		||||
		if !isIn || session == nil {
 | 
			
		||||
			// Check we haven't been given empty node ID, really this shouldn't ever
 | 
			
		||||
			// happen but just to be sure...
 | 
			
		||||
			if dstNodeID == nil || dstNodeIDMask == nil {
 | 
			
		||||
				panic("Given empty dstNodeID and dstNodeIDMask - this shouldn't happen")
 | 
			
		||||
			}
 | 
			
		||||
			// Dial to the remote node
 | 
			
		||||
			go func() {
 | 
			
		||||
				// FIXME just spitting out a goroutine to do this is kind of ugly and means we drop packets until the dial finishes
 | 
			
		||||
				tun.mutex.Lock()
 | 
			
		||||
				_, known := tun.dials[*dstNodeID]
 | 
			
		||||
				tun.dials[*dstNodeID] = append(tun.dials[*dstNodeID], bs)
 | 
			
		||||
				for len(tun.dials[*dstNodeID]) > 32 {
 | 
			
		||||
					util.PutBytes(tun.dials[*dstNodeID][0])
 | 
			
		||||
					tun.dials[*dstNodeID] = tun.dials[*dstNodeID][1:]
 | 
			
		||||
				}
 | 
			
		||||
				tun.mutex.Unlock()
 | 
			
		||||
				if known {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				var tc *tunConn
 | 
			
		||||
				if conn, err := tun.dialer.DialByNodeIDandMask(dstNodeID, dstNodeIDMask); err == nil {
 | 
			
		||||
					// We've been given a connection so prepare the session wrapper
 | 
			
		||||
					if tc, err = tun.wrap(conn); err != nil {
 | 
			
		||||
						// Something went wrong when storing the connection, typically that
 | 
			
		||||
						// something already exists for this address or subnet
 | 
			
		||||
						tun.log.Debugln("TUN/TAP iface wrap:", err)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				tun.mutex.Lock()
 | 
			
		||||
				packets := tun.dials[*dstNodeID]
 | 
			
		||||
				delete(tun.dials, *dstNodeID)
 | 
			
		||||
				tun.mutex.Unlock()
 | 
			
		||||
				if tc != nil {
 | 
			
		||||
					for _, packet := range packets {
 | 
			
		||||
						select {
 | 
			
		||||
						case tc.send <- packet:
 | 
			
		||||
						default:
 | 
			
		||||
							util.PutBytes(packet)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
			// While the dial is going on we can't do much else
 | 
			
		||||
			// continuing this iteration - skip to the next one
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// If we have a connection now, try writing to it
 | 
			
		||||
		if isIn && session != nil {
 | 
			
		||||
			select {
 | 
			
		||||
			case session.send <- bs:
 | 
			
		||||
			default:
 | 
			
		||||
				util.PutBytes(bs)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tun *TunAdapter) reader() error {
 | 
			
		||||
	recvd := make([]byte, 65535+tun_ETHER_HEADER_LENGTH)
 | 
			
		||||
	toWorker := make(chan []byte, 32)
 | 
			
		||||
	defer close(toWorker)
 | 
			
		||||
	go tun.readerPacketHandler(toWorker)
 | 
			
		||||
	for {
 | 
			
		||||
		// Wait for a packet to be delivered to us through the TUN/TAP adapter
 | 
			
		||||
		n, err := tun.iface.Read(recvd)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if !tun.isOpen {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		if n == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		bs := append(util.GetBytes(), recvd[:n]...)
 | 
			
		||||
		toWorker <- bs
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue