mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 03:05:07 +03:00 
			
		
		
		
	Merge pull request #491 from Arceliar/flowkey
Fix the old flowkey stuff so congestion control actually works...
This commit is contained in:
		
						commit
						c99ed9fb60
					
				
					 5 changed files with 105 additions and 52 deletions
				
			
		| 
						 | 
					@ -96,8 +96,11 @@ func (s *tunConn) writer() error {
 | 
				
			||||||
			if !ok {
 | 
								if !ok {
 | 
				
			||||||
				return errors.New("send closed")
 | 
									return errors.New("send closed")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// TODO write timeout and close
 | 
								msg := yggdrasil.FlowKeyMessage{
 | 
				
			||||||
			if err := s.conn.WriteNoCopy(bs); err != nil {
 | 
									FlowKey: util.GetFlowKey(bs),
 | 
				
			||||||
 | 
									Message: bs,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err := s.conn.WriteNoCopy(msg); err != nil {
 | 
				
			||||||
				if e, eok := err.(yggdrasil.ConnError); !eok {
 | 
									if e, eok := err.(yggdrasil.ConnError); !eok {
 | 
				
			||||||
					if e.Closed() {
 | 
										if e.Closed() {
 | 
				
			||||||
						s.tun.log.Debugln(s.conn.String(), "TUN/TAP generic write debug:", err)
 | 
											s.tun.log.Debugln(s.conn.String(), "TUN/TAP generic write debug:", err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,3 +106,41 @@ func DecodeCoordString(in string) (out []uint64) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return out
 | 
						return out
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetFlowLabel takes an IP packet as an argument and returns some information about the traffic flow.
 | 
				
			||||||
 | 
					// For IPv4 packets, this is derived from the source and destination protocol and port numbers.
 | 
				
			||||||
 | 
					// For IPv6 packets, this is derived from the FlowLabel field of the packet if this was set, otherwise it's handled like IPv4.
 | 
				
			||||||
 | 
					// The FlowKey is then used internally by Yggdrasil for congestion control.
 | 
				
			||||||
 | 
					func GetFlowKey(bs []byte) uint64 {
 | 
				
			||||||
 | 
						// Work out the flowkey - this is used to determine which switch queue
 | 
				
			||||||
 | 
						// traffic will be pushed to in the event of congestion
 | 
				
			||||||
 | 
						var flowkey uint64
 | 
				
			||||||
 | 
						// Get the IP protocol version from the packet
 | 
				
			||||||
 | 
						switch bs[0] & 0xf0 {
 | 
				
			||||||
 | 
						case 0x40: // IPv4 packet
 | 
				
			||||||
 | 
							// Check the packet meets minimum UDP packet length
 | 
				
			||||||
 | 
							if len(bs) >= 24 {
 | 
				
			||||||
 | 
								// Is the protocol TCP, UDP or SCTP?
 | 
				
			||||||
 | 
								if bs[9] == 0x06 || bs[9] == 0x11 || bs[9] == 0x84 {
 | 
				
			||||||
 | 
									ihl := bs[0] & 0x0f * 4 // Header length
 | 
				
			||||||
 | 
									flowkey = uint64(bs[9])<<32 /* proto */ |
 | 
				
			||||||
 | 
										uint64(bs[ihl+0])<<24 | uint64(bs[ihl+1])<<16 /* sport */ |
 | 
				
			||||||
 | 
										uint64(bs[ihl+2])<<8 | uint64(bs[ihl+3]) /* dport */
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case 0x60: // IPv6 packet
 | 
				
			||||||
 | 
							// Check if the flowlabel was specified in the packet header
 | 
				
			||||||
 | 
							flowkey = uint64(bs[1]&0x0f)<<16 | uint64(bs[2])<<8 | uint64(bs[3])
 | 
				
			||||||
 | 
							// If the flowlabel isn't present, make protokey from proto | sport | dport
 | 
				
			||||||
 | 
							// if the packet meets minimum UDP packet length
 | 
				
			||||||
 | 
							if flowkey == 0 && len(bs) >= 48 {
 | 
				
			||||||
 | 
								// Is the protocol TCP, UDP or SCTP?
 | 
				
			||||||
 | 
								if bs[6] == 0x06 || bs[6] == 0x11 || bs[6] == 0x84 {
 | 
				
			||||||
 | 
									flowkey = uint64(bs[6])<<32 /* proto */ |
 | 
				
			||||||
 | 
										uint64(bs[40])<<24 | uint64(bs[41])<<16 /* sport */ |
 | 
				
			||||||
 | 
										uint64(bs[42])<<8 | uint64(bs[43]) /* dport */
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return flowkey
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,11 +183,11 @@ func (c *Conn) Read(b []byte) (int, error) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Used internally by Write, the caller must not reuse the argument bytes when no error occurs
 | 
					// Used internally by Write, the caller must not reuse the argument bytes when no error occurs
 | 
				
			||||||
func (c *Conn) WriteNoCopy(bs []byte) error {
 | 
					func (c *Conn) WriteNoCopy(msg FlowKeyMessage) error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	sessionFunc := func() {
 | 
						sessionFunc := func() {
 | 
				
			||||||
		// Does the packet exceed the permitted size for the session?
 | 
							// Does the packet exceed the permitted size for the session?
 | 
				
			||||||
		if uint16(len(bs)) > c.session.getMTU() {
 | 
							if uint16(len(msg.Message)) > c.session.getMTU() {
 | 
				
			||||||
			err = ConnError{errors.New("packet too big"), true, false, false, int(c.session.getMTU())}
 | 
								err = ConnError{errors.New("packet too big"), true, false, false, int(c.session.getMTU())}
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -216,7 +216,7 @@ func (c *Conn) WriteNoCopy(bs []byte) error {
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				err = ConnError{errors.New("session closed"), false, false, true, 0}
 | 
									err = ConnError{errors.New("session closed"), false, false, true, 0}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case c.session.send <- bs:
 | 
							case c.session.send <- msg:
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
| 
						 | 
					@ -225,10 +225,10 @@ func (c *Conn) WriteNoCopy(bs []byte) error {
 | 
				
			||||||
// Implements net.Conn.Write
 | 
					// Implements net.Conn.Write
 | 
				
			||||||
func (c *Conn) Write(b []byte) (int, error) {
 | 
					func (c *Conn) Write(b []byte) (int, error) {
 | 
				
			||||||
	written := len(b)
 | 
						written := len(b)
 | 
				
			||||||
	bs := append(util.GetBytes(), b...)
 | 
						msg := FlowKeyMessage{Message: append(util.GetBytes(), b...)}
 | 
				
			||||||
	err := c.WriteNoCopy(bs)
 | 
						err := c.WriteNoCopy(msg)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		util.PutBytes(bs)
 | 
							util.PutBytes(msg.Message)
 | 
				
			||||||
		written = 0
 | 
							written = 0
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return written, err
 | 
						return written, err
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -166,7 +166,7 @@ func (r *router) handleTraffic(packet []byte) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	select {
 | 
						select {
 | 
				
			||||||
	case sinfo.fromRouter <- &p:
 | 
						case sinfo.fromRouter <- p:
 | 
				
			||||||
	case <-sinfo.cancel.Finished():
 | 
						case <-sinfo.cancel.Finished():
 | 
				
			||||||
		util.PutBytes(p.Payload)
 | 
							util.PutBytes(p.Payload)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,39 +18,39 @@ import (
 | 
				
			||||||
// All the information we know about an active session.
 | 
					// All the information we know about an active session.
 | 
				
			||||||
// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
 | 
					// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
 | 
				
			||||||
type sessionInfo struct {
 | 
					type sessionInfo struct {
 | 
				
			||||||
	mutex          sync.Mutex               // Protects all of the below, use it any time you read/chance the contents of a session
 | 
						mutex          sync.Mutex              // Protects all of the below, use it any time you read/chance the contents of a session
 | 
				
			||||||
	core           *Core                    //
 | 
						core           *Core                   //
 | 
				
			||||||
	reconfigure    chan chan error          //
 | 
						reconfigure    chan chan error         //
 | 
				
			||||||
	theirAddr      address.Address          //
 | 
						theirAddr      address.Address         //
 | 
				
			||||||
	theirSubnet    address.Subnet           //
 | 
						theirSubnet    address.Subnet          //
 | 
				
			||||||
	theirPermPub   crypto.BoxPubKey         //
 | 
						theirPermPub   crypto.BoxPubKey        //
 | 
				
			||||||
	theirSesPub    crypto.BoxPubKey         //
 | 
						theirSesPub    crypto.BoxPubKey        //
 | 
				
			||||||
	mySesPub       crypto.BoxPubKey         //
 | 
						mySesPub       crypto.BoxPubKey        //
 | 
				
			||||||
	mySesPriv      crypto.BoxPrivKey        //
 | 
						mySesPriv      crypto.BoxPrivKey       //
 | 
				
			||||||
	sharedSesKey   crypto.BoxSharedKey      // derived from session keys
 | 
						sharedSesKey   crypto.BoxSharedKey     // derived from session keys
 | 
				
			||||||
	theirHandle    crypto.Handle            //
 | 
						theirHandle    crypto.Handle           //
 | 
				
			||||||
	myHandle       crypto.Handle            //
 | 
						myHandle       crypto.Handle           //
 | 
				
			||||||
	theirNonce     crypto.BoxNonce          //
 | 
						theirNonce     crypto.BoxNonce         //
 | 
				
			||||||
	theirNonceMask uint64                   //
 | 
						theirNonceMask uint64                  //
 | 
				
			||||||
	myNonce        crypto.BoxNonce          //
 | 
						myNonce        crypto.BoxNonce         //
 | 
				
			||||||
	theirMTU       uint16                   //
 | 
						theirMTU       uint16                  //
 | 
				
			||||||
	myMTU          uint16                   //
 | 
						myMTU          uint16                  //
 | 
				
			||||||
	wasMTUFixed    bool                     // Was the MTU fixed by a receive error?
 | 
						wasMTUFixed    bool                    // Was the MTU fixed by a receive error?
 | 
				
			||||||
	timeOpened     time.Time                // Time the sessino was opened
 | 
						timeOpened     time.Time               // Time the sessino was opened
 | 
				
			||||||
	time           time.Time                // Time we last received a packet
 | 
						time           time.Time               // Time we last received a packet
 | 
				
			||||||
	mtuTime        time.Time                // time myMTU was last changed
 | 
						mtuTime        time.Time               // time myMTU was last changed
 | 
				
			||||||
	pingTime       time.Time                // time the first ping was sent since the last received packet
 | 
						pingTime       time.Time               // time the first ping was sent since the last received packet
 | 
				
			||||||
	pingSend       time.Time                // time the last ping was sent
 | 
						pingSend       time.Time               // time the last ping was sent
 | 
				
			||||||
	coords         []byte                   // coords of destination
 | 
						coords         []byte                  // coords of destination
 | 
				
			||||||
	reset          bool                     // reset if coords change
 | 
						reset          bool                    // reset if coords change
 | 
				
			||||||
	tstamp         int64                    // ATOMIC - tstamp from their last session ping, replay attack mitigation
 | 
						tstamp         int64                   // ATOMIC - tstamp from their last session ping, replay attack mitigation
 | 
				
			||||||
	bytesSent      uint64                   // Bytes of real traffic sent in this session
 | 
						bytesSent      uint64                  // Bytes of real traffic sent in this session
 | 
				
			||||||
	bytesRecvd     uint64                   // Bytes of real traffic received in this session
 | 
						bytesRecvd     uint64                  // Bytes of real traffic received in this session
 | 
				
			||||||
	fromRouter     chan *wire_trafficPacket // Received packets go here, picked up by the associated Conn
 | 
						init           chan struct{}           // Closed when the first session pong arrives, used to signal that the session is ready for initial use
 | 
				
			||||||
	init           chan struct{}            // Closed when the first session pong arrives, used to signal that the session is ready for initial use
 | 
						cancel         util.Cancellation       // Used to terminate workers
 | 
				
			||||||
	cancel         util.Cancellation        // Used to terminate workers
 | 
						fromRouter     chan wire_trafficPacket // Received packets go here, to be decrypted by the session
 | 
				
			||||||
	recv           chan []byte
 | 
						recv           chan []byte             // Decrypted packets go here, picked up by the associated Conn
 | 
				
			||||||
	send           chan []byte
 | 
						send           chan FlowKeyMessage     // Packets with optional flow key go here, to be encrypted and sent
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (sinfo *sessionInfo) doFunc(f func()) {
 | 
					func (sinfo *sessionInfo) doFunc(f func()) {
 | 
				
			||||||
| 
						 | 
					@ -228,9 +228,9 @@ func (ss *sessions) createSession(theirPermKey *crypto.BoxPubKey) *sessionInfo {
 | 
				
			||||||
	sinfo.myHandle = *crypto.NewHandle()
 | 
						sinfo.myHandle = *crypto.NewHandle()
 | 
				
			||||||
	sinfo.theirAddr = *address.AddrForNodeID(crypto.GetNodeID(&sinfo.theirPermPub))
 | 
						sinfo.theirAddr = *address.AddrForNodeID(crypto.GetNodeID(&sinfo.theirPermPub))
 | 
				
			||||||
	sinfo.theirSubnet = *address.SubnetForNodeID(crypto.GetNodeID(&sinfo.theirPermPub))
 | 
						sinfo.theirSubnet = *address.SubnetForNodeID(crypto.GetNodeID(&sinfo.theirPermPub))
 | 
				
			||||||
	sinfo.fromRouter = make(chan *wire_trafficPacket, 1)
 | 
						sinfo.fromRouter = make(chan wire_trafficPacket, 1)
 | 
				
			||||||
	sinfo.recv = make(chan []byte, 32)
 | 
						sinfo.recv = make(chan []byte, 32)
 | 
				
			||||||
	sinfo.send = make(chan []byte, 32)
 | 
						sinfo.send = make(chan FlowKeyMessage, 32)
 | 
				
			||||||
	ss.sinfos[sinfo.myHandle] = &sinfo
 | 
						ss.sinfos[sinfo.myHandle] = &sinfo
 | 
				
			||||||
	ss.byTheirPerm[sinfo.theirPermPub] = &sinfo.myHandle
 | 
						ss.byTheirPerm[sinfo.theirPermPub] = &sinfo.myHandle
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
| 
						 | 
					@ -442,13 +442,18 @@ func (sinfo *sessionInfo) startWorkers() {
 | 
				
			||||||
	go sinfo.sendWorker()
 | 
						go sinfo.sendWorker()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type FlowKeyMessage struct {
 | 
				
			||||||
 | 
						FlowKey uint64
 | 
				
			||||||
 | 
						Message []byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (sinfo *sessionInfo) recvWorker() {
 | 
					func (sinfo *sessionInfo) recvWorker() {
 | 
				
			||||||
	// TODO move theirNonce etc into a struct that gets stored here, passed in over a channel
 | 
						// TODO move theirNonce etc into a struct that gets stored here, passed in over a channel
 | 
				
			||||||
	//  Since there's no reason for anywhere else in the session code to need to *read* it...
 | 
						//  Since there's no reason for anywhere else in the session code to need to *read* it...
 | 
				
			||||||
	//  Only needs to be updated from the outside if a ping resets it...
 | 
						//  Only needs to be updated from the outside if a ping resets it...
 | 
				
			||||||
	//  That would get rid of the need to take a mutex for the sessionFunc
 | 
						//  That would get rid of the need to take a mutex for the sessionFunc
 | 
				
			||||||
	var callbacks []chan func()
 | 
						var callbacks []chan func()
 | 
				
			||||||
	doRecv := func(p *wire_trafficPacket) {
 | 
						doRecv := func(p wire_trafficPacket) {
 | 
				
			||||||
		var bs []byte
 | 
							var bs []byte
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var k crypto.BoxSharedKey
 | 
							var k crypto.BoxSharedKey
 | 
				
			||||||
| 
						 | 
					@ -524,16 +529,22 @@ func (sinfo *sessionInfo) sendWorker() {
 | 
				
			||||||
	// TODO move info that this worker needs here, send updates via a channel
 | 
						// TODO move info that this worker needs here, send updates via a channel
 | 
				
			||||||
	//  Otherwise we need to take a mutex to avoid races with update()
 | 
						//  Otherwise we need to take a mutex to avoid races with update()
 | 
				
			||||||
	var callbacks []chan func()
 | 
						var callbacks []chan func()
 | 
				
			||||||
	doSend := func(bs []byte) {
 | 
						doSend := func(msg FlowKeyMessage) {
 | 
				
			||||||
		var p wire_trafficPacket
 | 
							var p wire_trafficPacket
 | 
				
			||||||
		var k crypto.BoxSharedKey
 | 
							var k crypto.BoxSharedKey
 | 
				
			||||||
		sessionFunc := func() {
 | 
							sessionFunc := func() {
 | 
				
			||||||
			sinfo.bytesSent += uint64(len(bs))
 | 
								sinfo.bytesSent += uint64(len(msg.Message))
 | 
				
			||||||
			p = wire_trafficPacket{
 | 
								p = wire_trafficPacket{
 | 
				
			||||||
				Coords: append([]byte(nil), sinfo.coords...),
 | 
									Coords: append([]byte(nil), sinfo.coords...),
 | 
				
			||||||
				Handle: sinfo.theirHandle,
 | 
									Handle: sinfo.theirHandle,
 | 
				
			||||||
				Nonce:  sinfo.myNonce,
 | 
									Nonce:  sinfo.myNonce,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if msg.FlowKey != 0 {
 | 
				
			||||||
 | 
									// Helps ensure that traffic from this flow ends up in a separate queue from other flows
 | 
				
			||||||
 | 
									// The zero padding relies on the fact that the self-peer is always on port 0
 | 
				
			||||||
 | 
									p.Coords = append(p.Coords, 0)
 | 
				
			||||||
 | 
									p.Coords = wire_put_uint64(msg.FlowKey, p.Coords)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			sinfo.myNonce.Increment()
 | 
								sinfo.myNonce.Increment()
 | 
				
			||||||
			k = sinfo.sharedSesKey
 | 
								k = sinfo.sharedSesKey
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -542,12 +553,13 @@ func (sinfo *sessionInfo) sendWorker() {
 | 
				
			||||||
		ch := make(chan func(), 1)
 | 
							ch := make(chan func(), 1)
 | 
				
			||||||
		poolFunc := func() {
 | 
							poolFunc := func() {
 | 
				
			||||||
			// Encrypt the packet
 | 
								// Encrypt the packet
 | 
				
			||||||
			p.Payload, _ = crypto.BoxSeal(&k, bs, &p.Nonce)
 | 
								p.Payload, _ = crypto.BoxSeal(&k, msg.Message, &p.Nonce)
 | 
				
			||||||
			packet := p.encode()
 | 
					 | 
				
			||||||
			// The callback will send the packet
 | 
								// The callback will send the packet
 | 
				
			||||||
			callback := func() {
 | 
								callback := func() {
 | 
				
			||||||
 | 
									// Encoding may block on a util.GetBytes(), so kept out of the worker pool
 | 
				
			||||||
 | 
									packet := p.encode()
 | 
				
			||||||
				// Cleanup
 | 
									// Cleanup
 | 
				
			||||||
				util.PutBytes(bs)
 | 
									util.PutBytes(msg.Message)
 | 
				
			||||||
				util.PutBytes(p.Payload)
 | 
									util.PutBytes(p.Payload)
 | 
				
			||||||
				// Send the packet
 | 
									// Send the packet
 | 
				
			||||||
				sinfo.core.router.out(packet)
 | 
									sinfo.core.router.out(packet)
 | 
				
			||||||
| 
						 | 
					@ -566,8 +578,8 @@ func (sinfo *sessionInfo) sendWorker() {
 | 
				
			||||||
				f()
 | 
									f()
 | 
				
			||||||
			case <-sinfo.cancel.Finished():
 | 
								case <-sinfo.cancel.Finished():
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			case bs := <-sinfo.send:
 | 
								case msg := <-sinfo.send:
 | 
				
			||||||
				doSend(bs)
 | 
									doSend(msg)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue