Handshake negotiation functioning
This commit is contained in:
		
							parent
							
								
									a4cc0a30fa
								
							
						
					
					
						commit
						1e620427bd
					
				
					 10 changed files with 512 additions and 82 deletions
				
			
		|  | @ -18,4 +18,8 @@ const ( | |||
| 
 | ||||
| const ( | ||||
| 	QueueOutboundSize      = 1024 | ||||
| 	QueueInboundSize       = 1024 | ||||
| 	QueueHandshakeSize     = 1024 | ||||
| 	QueueHandshakeBusySize = QueueHandshakeSize / 8 | ||||
| 	MinMessageSize         = MessageTransportSize | ||||
| ) | ||||
|  |  | |||
|  | @ -23,7 +23,13 @@ type Device struct { | |||
| 	routingTable RoutingTable | ||||
| 	indices      IndexTable | ||||
| 	queue        struct { | ||||
| 		encryption chan *QueueOutboundElement // parallel work queue
 | ||||
| 		encryption chan *QueueOutboundElement | ||||
| 		decryption chan *QueueInboundElement | ||||
| 		handshake  chan QueueHandshakeElement | ||||
| 		inbound    chan []byte // inbound queue for TUN
 | ||||
| 	} | ||||
| 	signal struct { | ||||
| 		stop chan struct{} | ||||
| 	} | ||||
| 	peers map[NoisePublicKey]*Peer | ||||
| 	mac   MacStateDevice | ||||
|  | @ -56,6 +62,7 @@ func NewDevice(tun TUNDevice, logLevel int) *Device { | |||
| 	defer device.mutex.Unlock() | ||||
| 
 | ||||
| 	device.log = NewLogger(logLevel) | ||||
| 	device.mtu = tun.MTU() | ||||
| 	device.peers = make(map[NoisePublicKey]*Peer) | ||||
| 	device.indices.Init() | ||||
| 	device.routingTable.Reset() | ||||
|  | @ -71,13 +78,22 @@ func NewDevice(tun TUNDevice, logLevel int) *Device { | |||
| 	// create queues
 | ||||
| 
 | ||||
| 	device.queue.encryption = make(chan *QueueOutboundElement, QueueOutboundSize) | ||||
| 	device.queue.handshake = make(chan QueueHandshakeElement, QueueHandshakeSize) | ||||
| 	device.queue.decryption = make(chan *QueueInboundElement, QueueInboundSize) | ||||
| 
 | ||||
| 	// prepare signals
 | ||||
| 
 | ||||
| 	device.signal.stop = make(chan struct{}) | ||||
| 
 | ||||
| 	// start workers
 | ||||
| 
 | ||||
| 	for i := 0; i < runtime.NumCPU(); i += 1 { | ||||
| 		go device.RoutineEncryption() | ||||
| 		go device.RoutineDecryption() | ||||
| 		go device.RoutineHandshake() | ||||
| 	} | ||||
| 	go device.RoutineReadFromTUN(tun) | ||||
| 	go device.RoutineReceiveIncomming() | ||||
| 	return device | ||||
| } | ||||
| 
 | ||||
|  | @ -115,5 +131,6 @@ func (device *Device) RemoveAllPeers() { | |||
| 
 | ||||
| func (device *Device) Close() { | ||||
| 	device.RemoveAllPeers() | ||||
| 	close(device.signal.stop) | ||||
| 	close(device.queue.encryption) | ||||
| } | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ package main | |||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"net" | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
| ) | ||||
|  | @ -24,14 +23,6 @@ func (peer *Peer) SendKeepAlive() bool { | |||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func StoppedTimer() *time.Timer { | ||||
| 	timer := time.NewTimer(time.Hour) | ||||
| 	if !timer.Stop() { | ||||
| 		<-timer.C | ||||
| 	} | ||||
| 	return timer | ||||
| } | ||||
| 
 | ||||
| /* Called when a new authenticated message has been send | ||||
|  * | ||||
|  * TODO: This might be done in a faster way | ||||
|  | @ -71,7 +62,7 @@ func (peer *Peer) RoutineHandshakeInitiator() { | |||
| 	device := peer.device | ||||
| 	buffer := make([]byte, 1024) | ||||
| 	logger := device.log.Debug | ||||
| 	timeout := time.NewTimer(time.Hour) | ||||
| 	timeout := stoppedTimer() | ||||
| 
 | ||||
| 	var work *QueueOutboundElement | ||||
| 
 | ||||
|  | @ -129,13 +120,8 @@ func (peer *Peer) RoutineHandshakeInitiator() { | |||
| 
 | ||||
| 				// set timeout
 | ||||
| 
 | ||||
| 				if !timeout.Stop() { | ||||
| 					select { | ||||
| 					case <-timeout.C: | ||||
| 					default: | ||||
| 					} | ||||
| 				} | ||||
| 				attempts += 1 | ||||
| 				stopTimer(timeout) | ||||
| 				timeout.Reset(RekeyTimeout) | ||||
| 				device.log.Debug.Println("Handshake initiation attempt", attempts, "queued for peer", peer.id) | ||||
| 
 | ||||
|  | @ -163,45 +149,3 @@ func (peer *Peer) RoutineHandshakeInitiator() { | |||
| 
 | ||||
| 	logger.Println("Routine, handshake initator, stopped for peer", peer.id) | ||||
| } | ||||
| 
 | ||||
| /* Handles incomming packets related to handshake | ||||
|  * | ||||
|  * | ||||
|  */ | ||||
| func (device *Device) HandshakeWorker(queue chan struct { | ||||
| 	msg     []byte | ||||
| 	msgType uint32 | ||||
| 	addr    *net.UDPAddr | ||||
| }) { | ||||
| 	for { | ||||
| 		elem := <-queue | ||||
| 
 | ||||
| 		switch elem.msgType { | ||||
| 		case MessageInitiationType: | ||||
| 			if len(elem.msg) != MessageInitiationSize { | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
| 			// check for cookie
 | ||||
| 
 | ||||
| 			var msg MessageInitiation | ||||
| 
 | ||||
| 			binary.Read(nil, binary.LittleEndian, &msg) | ||||
| 
 | ||||
| 		case MessageResponseType: | ||||
| 			if len(elem.msg) != MessageResponseSize { | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
| 			// check for cookie
 | ||||
| 
 | ||||
| 		case MessageCookieReplyType: | ||||
| 			if len(elem.msg) != MessageCookieReplySize { | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
| 		default: | ||||
| 			device.log.Error.Println("Invalid message type in handshake queue") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										19
									
								
								src/misc.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/misc.go
									
									
									
									
									
								
							|  | @ -1,5 +1,9 @@ | |||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| func min(a uint, b uint) uint { | ||||
| 	if a > b { | ||||
| 		return b | ||||
|  | @ -13,3 +17,18 @@ func sendSignal(c chan struct{}) { | |||
| 	default: | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func stopTimer(timer *time.Timer) { | ||||
| 	if !timer.Stop() { | ||||
| 		select { | ||||
| 		case <-timer.C: | ||||
| 		default: | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func stoppedTimer() *time.Timer { | ||||
| 	timer := time.NewTimer(time.Hour) | ||||
| 	stopTimer(timer) | ||||
| 	return timer | ||||
| } | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import ( | |||
| 	"golang.org/x/crypto/chacha20poly1305" | ||||
| 	"golang.org/x/crypto/poly1305" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
|  | @ -34,6 +35,13 @@ const ( | |||
| 	MessageInitiationSize  = 148 | ||||
| 	MessageResponseSize    = 92 | ||||
| 	MessageCookieReplySize = 64 | ||||
| 	MessageTransportSize   = 16 + poly1305.TagSize // size of empty transport
 | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	MessageTransportOffsetReceiver = 4 | ||||
| 	MessageTransportOffsetCounter  = 8 | ||||
| 	MessageTransportOffsetContent  = 16 | ||||
| ) | ||||
| 
 | ||||
| /* Type is an 8-bit field, followed by 3 nul bytes, | ||||
|  | @ -55,7 +63,7 @@ type MessageInitiation struct { | |||
| type MessageResponse struct { | ||||
| 	Type      uint32 | ||||
| 	Sender    uint32 | ||||
| 	Reciever  uint32 | ||||
| 	Receiver  uint32 | ||||
| 	Ephemeral NoisePublicKey | ||||
| 	Empty     [poly1305.TagSize]byte | ||||
| 	Mac1      [blake2s.Size128]byte | ||||
|  | @ -64,7 +72,7 @@ type MessageResponse struct { | |||
| 
 | ||||
| type MessageTransport struct { | ||||
| 	Type     uint32 | ||||
| 	Reciever uint32 | ||||
| 	Receiver uint32 | ||||
| 	Counter  uint64 | ||||
| 	Content  []byte | ||||
| } | ||||
|  | @ -292,7 +300,7 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error | |||
| 	var msg MessageResponse | ||||
| 	msg.Type = MessageResponseType | ||||
| 	msg.Sender = handshake.localIndex | ||||
| 	msg.Reciever = handshake.remoteIndex | ||||
| 	msg.Receiver = handshake.remoteIndex | ||||
| 
 | ||||
| 	// create ephemeral key
 | ||||
| 
 | ||||
|  | @ -302,6 +310,7 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error | |||
| 	} | ||||
| 	msg.Ephemeral = handshake.localEphemeral.publicKey() | ||||
| 	handshake.mixHash(msg.Ephemeral[:]) | ||||
| 	handshake.mixKey(msg.Ephemeral[:]) | ||||
| 
 | ||||
| 	func() { | ||||
| 		ss := handshake.localEphemeral.sharedSecret(handshake.remoteEphemeral) | ||||
|  | @ -334,7 +343,7 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer { | |||
| 
 | ||||
| 	// lookup handshake by reciever
 | ||||
| 
 | ||||
| 	lookup := device.indices.Lookup(msg.Reciever) | ||||
| 	lookup := device.indices.Lookup(msg.Receiver) | ||||
| 	handshake := lookup.handshake | ||||
| 	if handshake == nil { | ||||
| 		return nil | ||||
|  | @ -359,7 +368,7 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer { | |||
| 		// finish 3-way DH
 | ||||
| 
 | ||||
| 		hash = mixHash(handshake.hash, msg.Ephemeral[:]) | ||||
| 		chainKey = handshake.chainKey | ||||
| 		chainKey = mixKey(handshake.chainKey, msg.Ephemeral[:]) | ||||
| 
 | ||||
| 		func() { | ||||
| 			ss := handshake.localEphemeral.sharedSecret(msg.Ephemeral) | ||||
|  | @ -380,6 +389,7 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer { | |||
| 		aead, _ := chacha20poly1305.New(key[:]) | ||||
| 		_, err := aead.Open(nil, ZeroNonce[:], msg.Empty[:], hash[:]) | ||||
| 		if err != nil { | ||||
| 			device.log.Debug.Println("failed to open") | ||||
| 			return false | ||||
| 		} | ||||
| 		hash = mixHash(hash, msg.Empty[:]) | ||||
|  | @ -438,6 +448,7 @@ func (peer *Peer) NewKeyPair() *KeyPair { | |||
| 	keyPair.recv, _ = chacha20poly1305.New(recvKey[:]) | ||||
| 	keyPair.sendNonce = 0 | ||||
| 	keyPair.recvNonce = 0 | ||||
| 	keyPair.created = time.Now() | ||||
| 
 | ||||
| 	// remap index
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										15
									
								
								src/peer.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/peer.go
									
									
									
									
									
								
							|  | @ -37,6 +37,7 @@ type Peer struct { | |||
| 	queue struct { | ||||
| 		nonce    chan []byte                // nonce / pre-handshake queue
 | ||||
| 		outbound chan *QueueOutboundElement // sequential ordering of work
 | ||||
| 		inbound  chan *QueueInboundElement  // sequential ordering of work
 | ||||
| 	} | ||||
| 	mac MacStatePeer | ||||
| } | ||||
|  | @ -47,11 +48,10 @@ func (device *Device) NewPeer(pk NoisePublicKey) *Peer { | |||
| 	peer := new(Peer) | ||||
| 	peer.mutex.Lock() | ||||
| 	defer peer.mutex.Unlock() | ||||
| 	peer.device = device | ||||
| 
 | ||||
| 	peer.mac.Init(pk) | ||||
| 	peer.queue.outbound = make(chan *QueueOutboundElement, QueueOutboundSize) | ||||
| 	peer.queue.nonce = make(chan []byte, QueueOutboundSize) | ||||
| 	peer.timer.sendKeepalive = StoppedTimer() | ||||
| 	peer.device = device | ||||
| 	peer.timer.sendKeepalive = stoppedTimer() | ||||
| 
 | ||||
| 	// assign id for debugging
 | ||||
| 
 | ||||
|  | @ -76,6 +76,12 @@ func (device *Device) NewPeer(pk NoisePublicKey) *Peer { | |||
| 	handshake.precomputedStaticStatic = device.privateKey.sharedSecret(handshake.remoteStatic) | ||||
| 	handshake.mutex.Unlock() | ||||
| 
 | ||||
| 	// prepare queuing
 | ||||
| 
 | ||||
| 	peer.queue.nonce = make(chan []byte, QueueOutboundSize) | ||||
| 	peer.queue.inbound = make(chan *QueueInboundElement, QueueInboundSize) | ||||
| 	peer.queue.outbound = make(chan *QueueOutboundElement, QueueOutboundSize) | ||||
| 
 | ||||
| 	// prepare signaling
 | ||||
| 
 | ||||
| 	peer.signal.stop = make(chan struct{}) | ||||
|  | @ -89,6 +95,7 @@ func (device *Device) NewPeer(pk NoisePublicKey) *Peer { | |||
| 	go peer.RoutineNonce() | ||||
| 	go peer.RoutineHandshakeInitiator() | ||||
| 	go peer.RoutineSequentialSender() | ||||
| 	go peer.RoutineSequentialReceiver() | ||||
| 
 | ||||
| 	return peer | ||||
| } | ||||
|  |  | |||
							
								
								
									
										404
									
								
								src/receive.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								src/receive.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,404 @@ | |||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"golang.org/x/crypto/chacha20poly1305" | ||||
| 	"net" | ||||
| 	"sync" | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	ElementStateOkay = iota | ||||
| 	ElementStateDropped | ||||
| ) | ||||
| 
 | ||||
| type QueueHandshakeElement struct { | ||||
| 	msgType uint32 | ||||
| 	packet  []byte | ||||
| 	source  *net.UDPAddr | ||||
| } | ||||
| 
 | ||||
| type QueueInboundElement struct { | ||||
| 	state   uint32 | ||||
| 	mutex   sync.Mutex | ||||
| 	packet  []byte | ||||
| 	counter uint64 | ||||
| 	keyPair *KeyPair | ||||
| } | ||||
| 
 | ||||
| func (elem *QueueInboundElement) Drop() { | ||||
| 	atomic.StoreUint32(&elem.state, ElementStateDropped) | ||||
| 	elem.mutex.Unlock() | ||||
| } | ||||
| 
 | ||||
| func (device *Device) RoutineReceiveIncomming() { | ||||
| 	var packet []byte | ||||
| 
 | ||||
| 	debugLog := device.log.Debug | ||||
| 	debugLog.Println("Routine, receive incomming, started") | ||||
| 
 | ||||
| 	errorLog := device.log.Error | ||||
| 
 | ||||
| 	for { | ||||
| 
 | ||||
| 		// check if stopped
 | ||||
| 
 | ||||
| 		select { | ||||
| 		case <-device.signal.stop: | ||||
| 			return | ||||
| 		default: | ||||
| 		} | ||||
| 
 | ||||
| 		// read next datagram
 | ||||
| 
 | ||||
| 		if packet == nil { | ||||
| 			packet = make([]byte, 1<<16) | ||||
| 		} | ||||
| 
 | ||||
| 		device.net.mutex.RLock() | ||||
| 		conn := device.net.conn | ||||
| 		device.net.mutex.RUnlock() | ||||
| 
 | ||||
| 		conn.SetReadDeadline(time.Now().Add(time.Second)) | ||||
| 
 | ||||
| 		size, raddr, err := conn.ReadFromUDP(packet) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		if size < MinMessageSize { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// handle packet
 | ||||
| 
 | ||||
| 		packet = packet[:size] | ||||
| 		msgType := binary.LittleEndian.Uint32(packet[:4]) | ||||
| 
 | ||||
| 		func() { | ||||
| 			switch msgType { | ||||
| 
 | ||||
| 			case MessageInitiationType, MessageResponseType: | ||||
| 
 | ||||
| 				// verify mac1
 | ||||
| 
 | ||||
| 				if !device.mac.CheckMAC1(packet) { | ||||
| 					debugLog.Println("Received packet with invalid mac1") | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				// check if busy, TODO: refine definition of "busy"
 | ||||
| 
 | ||||
| 				busy := len(device.queue.handshake) > QueueHandshakeBusySize | ||||
| 				if busy && !device.mac.CheckMAC2(packet, raddr) { | ||||
| 					sender := binary.LittleEndian.Uint32(packet[4:8]) // "sender" follows "type"
 | ||||
| 					reply, err := device.CreateMessageCookieReply(packet, sender, raddr) | ||||
| 					if err != nil { | ||||
| 						errorLog.Println("Failed to create cookie reply:", err) | ||||
| 						return | ||||
| 					} | ||||
| 					writer := bytes.NewBuffer(packet[:0]) | ||||
| 					binary.Write(writer, binary.LittleEndian, reply) | ||||
| 					packet = writer.Bytes() | ||||
| 					_, err = device.net.conn.WriteToUDP(packet, raddr) | ||||
| 					if err != nil { | ||||
| 						debugLog.Println("Failed to send cookie reply:", err) | ||||
| 					} | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				// add to handshake queue
 | ||||
| 
 | ||||
| 				device.queue.handshake <- QueueHandshakeElement{ | ||||
| 					msgType: msgType, | ||||
| 					packet:  packet, | ||||
| 					source:  raddr, | ||||
| 				} | ||||
| 
 | ||||
| 			case MessageCookieReplyType: | ||||
| 
 | ||||
| 				// verify and update peer cookie state
 | ||||
| 
 | ||||
| 				if len(packet) != MessageCookieReplySize { | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				var reply MessageCookieReply | ||||
| 				reader := bytes.NewReader(packet) | ||||
| 				err := binary.Read(reader, binary.LittleEndian, &reply) | ||||
| 				if err != nil { | ||||
| 					debugLog.Println("Failed to decode cookie reply") | ||||
| 					return | ||||
| 				} | ||||
| 				device.ConsumeMessageCookieReply(&reply) | ||||
| 
 | ||||
| 			case MessageTransportType: | ||||
| 
 | ||||
| 				debugLog.Println("DEBUG: Got transport") | ||||
| 
 | ||||
| 				// lookup key pair
 | ||||
| 
 | ||||
| 				if len(packet) < MessageTransportSize { | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				receiver := binary.LittleEndian.Uint32( | ||||
| 					packet[MessageTransportOffsetReceiver:MessageTransportOffsetCounter], | ||||
| 				) | ||||
| 				value := device.indices.Lookup(receiver) | ||||
| 				keyPair := value.keyPair | ||||
| 				if keyPair == nil { | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				// check key-pair expiry
 | ||||
| 
 | ||||
| 				if keyPair.created.Add(RejectAfterTime).Before(time.Now()) { | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				// add to peer queue
 | ||||
| 
 | ||||
| 				peer := value.peer | ||||
| 				work := new(QueueInboundElement) | ||||
| 				work.packet = packet | ||||
| 				work.keyPair = keyPair | ||||
| 				work.state = ElementStateOkay | ||||
| 				work.mutex.Lock() | ||||
| 
 | ||||
| 				// add to parallel decryption queue
 | ||||
| 
 | ||||
| 				func() { | ||||
| 					for { | ||||
| 						select { | ||||
| 						case device.queue.decryption <- work: | ||||
| 							return | ||||
| 						default: | ||||
| 							select { | ||||
| 							case elem := <-device.queue.decryption: | ||||
| 								elem.Drop() | ||||
| 							default: | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				}() | ||||
| 
 | ||||
| 				// add to sequential inbound queue
 | ||||
| 
 | ||||
| 				func() { | ||||
| 					for { | ||||
| 						select { | ||||
| 						case peer.queue.inbound <- work: | ||||
| 							break | ||||
| 						default: | ||||
| 							select { | ||||
| 							case elem := <-peer.queue.inbound: | ||||
| 								elem.Drop() | ||||
| 							default: | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				}() | ||||
| 
 | ||||
| 			default: | ||||
| 				// unknown message type
 | ||||
| 			} | ||||
| 		}() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (device *Device) RoutineDecryption() { | ||||
| 	var elem *QueueInboundElement | ||||
| 	var nonce [chacha20poly1305.NonceSize]byte | ||||
| 
 | ||||
| 	for { | ||||
| 		select { | ||||
| 		case elem = <-device.queue.decryption: | ||||
| 		case <-device.signal.stop: | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// check if dropped
 | ||||
| 
 | ||||
| 		state := atomic.LoadUint32(&elem.state) | ||||
| 		if state != ElementStateOkay { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// split message into fields
 | ||||
| 
 | ||||
| 		counter := binary.LittleEndian.Uint64( | ||||
| 			elem.packet[MessageTransportOffsetCounter:MessageTransportOffsetContent], | ||||
| 		) | ||||
| 		content := elem.packet[MessageTransportOffsetContent:] | ||||
| 
 | ||||
| 		// decrypt with key-pair
 | ||||
| 
 | ||||
| 		var err error | ||||
| 		binary.LittleEndian.PutUint64(nonce[4:], counter) | ||||
| 		elem.packet, err = elem.keyPair.recv.Open(elem.packet[:0], nonce[:], content, nil) | ||||
| 		if err != nil { | ||||
| 			elem.Drop() | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// release to consumer
 | ||||
| 
 | ||||
| 		elem.counter = counter | ||||
| 		elem.mutex.Unlock() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Handles incomming packets related to handshake | ||||
|  * | ||||
|  * | ||||
|  */ | ||||
| func (device *Device) RoutineHandshake() { | ||||
| 
 | ||||
| 	logInfo := device.log.Info | ||||
| 	logError := device.log.Error | ||||
| 	logDebug := device.log.Debug | ||||
| 
 | ||||
| 	var elem QueueHandshakeElement | ||||
| 
 | ||||
| 	for { | ||||
| 		select { | ||||
| 		case elem = <-device.queue.handshake: | ||||
| 		case <-device.signal.stop: | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		func() { | ||||
| 
 | ||||
| 			switch elem.msgType { | ||||
| 			case MessageInitiationType: | ||||
| 
 | ||||
| 				// unmarshal
 | ||||
| 
 | ||||
| 				if len(elem.packet) != MessageInitiationSize { | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				var msg MessageInitiation | ||||
| 				reader := bytes.NewReader(elem.packet) | ||||
| 				err := binary.Read(reader, binary.LittleEndian, &msg) | ||||
| 				if err != nil { | ||||
| 					logError.Println("Failed to decode initiation message") | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				// consume initiation
 | ||||
| 
 | ||||
| 				peer := device.ConsumeMessageInitiation(&msg) | ||||
| 				if peer == nil { | ||||
| 					logInfo.Println( | ||||
| 						"Recieved invalid initiation message from", | ||||
| 						elem.source.IP.String(), | ||||
| 						elem.source.Port, | ||||
| 					) | ||||
| 					return | ||||
| 				} | ||||
| 				logDebug.Println("Recieved valid initiation message for peer", peer.id) | ||||
| 
 | ||||
| 			case MessageResponseType: | ||||
| 
 | ||||
| 				// unmarshal
 | ||||
| 
 | ||||
| 				if len(elem.packet) != MessageResponseSize { | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				var msg MessageResponse | ||||
| 				reader := bytes.NewReader(elem.packet) | ||||
| 				err := binary.Read(reader, binary.LittleEndian, &msg) | ||||
| 				if err != nil { | ||||
| 					logError.Println("Failed to decode response message") | ||||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				// consume response
 | ||||
| 
 | ||||
| 				peer := device.ConsumeMessageResponse(&msg) | ||||
| 				if peer == nil { | ||||
| 					logInfo.Println( | ||||
| 						"Recieved invalid response message from", | ||||
| 						elem.source.IP.String(), | ||||
| 						elem.source.Port, | ||||
| 					) | ||||
| 					return | ||||
| 				} | ||||
| 				sendSignal(peer.signal.handshakeCompleted) | ||||
| 				logDebug.Println("Recieved valid response message for peer", peer.id) | ||||
| 				peer.NewKeyPair() | ||||
| 				peer.SendKeepAlive() | ||||
| 
 | ||||
| 			default: | ||||
| 				device.log.Error.Println("Invalid message type in handshake queue") | ||||
| 			} | ||||
| 
 | ||||
| 		}() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (peer *Peer) RoutineSequentialReceiver() { | ||||
| 	var elem *QueueInboundElement | ||||
| 
 | ||||
| 	device := peer.device | ||||
| 	logDebug := device.log.Debug | ||||
| 
 | ||||
| 	logDebug.Println("Routine, sequential receiver, started for peer", peer.id) | ||||
| 
 | ||||
| 	for { | ||||
| 		// wait for decryption
 | ||||
| 
 | ||||
| 		select { | ||||
| 		case <-peer.signal.stop: | ||||
| 			return | ||||
| 		case elem = <-peer.queue.inbound: | ||||
| 		} | ||||
| 		elem.mutex.Lock() | ||||
| 
 | ||||
| 		// check if dropped
 | ||||
| 
 | ||||
| 		logDebug.Println("MESSSAGE:", elem) | ||||
| 
 | ||||
| 		state := atomic.LoadUint32(&elem.state) | ||||
| 		if state != ElementStateOkay { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// check for replay
 | ||||
| 
 | ||||
| 		// check for keep-alive
 | ||||
| 
 | ||||
| 		if len(elem.packet) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// insert into inbound TUN queue
 | ||||
| 
 | ||||
| 		device.queue.inbound <- elem.packet | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (device *Device) RoutineWriteToTUN(tun TUNDevice) { | ||||
| 	for { | ||||
| 		var packet []byte | ||||
| 
 | ||||
| 		select { | ||||
| 		case <-device.signal.stop: | ||||
| 		case packet = <-device.queue.inbound: | ||||
| 		} | ||||
| 
 | ||||
| 		device.log.Debug.Println("GOT:", packet) | ||||
| 
 | ||||
| 		size, err := tun.Write(packet) | ||||
| 		device.log.Debug.Println("DEBUG:", size, err) | ||||
| 		if err != nil { | ||||
| 
 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										44
									
								
								src/send.go
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								src/send.go
									
									
									
									
									
								
							|  | @ -27,6 +27,7 @@ import ( | |||
|  * workers release lock when they have completed work on the packet. | ||||
|  */ | ||||
| type QueueOutboundElement struct { | ||||
| 	state   uint32 | ||||
| 	mutex   sync.Mutex | ||||
| 	packet  []byte | ||||
| 	nonce   uint64 | ||||
|  | @ -59,6 +60,14 @@ func (peer *Peer) InsertOutbound(elem *QueueOutboundElement) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (elem *QueueOutboundElement) Drop() { | ||||
| 	atomic.StoreUint32(&elem.state, ElementStateDropped) | ||||
| } | ||||
| 
 | ||||
| func (elem *QueueOutboundElement) IsDropped() bool { | ||||
| 	return atomic.LoadUint32(&elem.state) == ElementStateDropped | ||||
| } | ||||
| 
 | ||||
| /* Reads packets from the TUN and inserts | ||||
|  * into nonce queue for peer | ||||
|  * | ||||
|  | @ -162,6 +171,8 @@ func (peer *Peer) RoutineNonce() { | |||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			logger.Println("PACKET:", packet) | ||||
| 
 | ||||
| 			// wait for key pair
 | ||||
| 
 | ||||
| 			for { | ||||
|  | @ -176,6 +187,7 @@ func (peer *Peer) RoutineNonce() { | |||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 				logger.Println("Key pair:", keyPair) | ||||
| 
 | ||||
| 				sendSignal(peer.signal.handshakeBegin) | ||||
| 				logger.Println("Waiting for key-pair, peer", peer.id) | ||||
|  | @ -205,10 +217,12 @@ func (peer *Peer) RoutineNonce() { | |||
| 				work := new(QueueOutboundElement) // TODO: profile, maybe use pool
 | ||||
| 				work.keyPair = keyPair | ||||
| 				work.packet = packet | ||||
| 				work.nonce = atomic.AddUint64(&keyPair.sendNonce, 1) | ||||
| 				work.nonce = atomic.AddUint64(&keyPair.sendNonce, 1) - 1 | ||||
| 				work.peer = peer | ||||
| 				work.mutex.Lock() | ||||
| 
 | ||||
| 				logger.Println("WORK:", work) | ||||
| 
 | ||||
| 				packet = nil | ||||
| 
 | ||||
| 				// drop packets until there is space
 | ||||
|  | @ -219,9 +233,11 @@ func (peer *Peer) RoutineNonce() { | |||
| 						case peer.device.queue.encryption <- work: | ||||
| 							return | ||||
| 						default: | ||||
| 							drop := <-peer.device.queue.encryption | ||||
| 							drop.packet = nil | ||||
| 							drop.mutex.Unlock() | ||||
| 							select { | ||||
| 							case elem := <-peer.device.queue.encryption: | ||||
| 								elem.Drop() | ||||
| 							default: | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				}() | ||||
|  | @ -241,18 +257,22 @@ func (peer *Peer) RoutineNonce() { | |||
| func (device *Device) RoutineEncryption() { | ||||
| 	var nonce [chacha20poly1305.NonceSize]byte | ||||
| 	for work := range device.queue.encryption { | ||||
| 		if work.IsDropped() { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// pad packet
 | ||||
| 
 | ||||
| 		padding := device.mtu - len(work.packet) | ||||
| 		if padding < 0 { | ||||
| 			// drop
 | ||||
| 			work.packet = nil | ||||
| 			work.mutex.Unlock() | ||||
| 			work.Drop() | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		for n := 0; n < padding; n += 1 { | ||||
| 			work.packet = append(work.packet, 0) | ||||
| 		} | ||||
| 		device.log.Debug.Println(work.packet) | ||||
| 
 | ||||
| 		// encrypt
 | ||||
| 
 | ||||
|  | @ -288,6 +308,9 @@ func (peer *Peer) RoutineSequentialSender() { | |||
| 			logger.Println("Routine, sequential sender, stopped for peer", peer.id) | ||||
| 			return | ||||
| 		case work := <-peer.queue.outbound: | ||||
| 			if work.IsDropped() { | ||||
| 				continue | ||||
| 			} | ||||
| 			work.mutex.Lock() | ||||
| 			func() { | ||||
| 				if work.packet == nil { | ||||
|  | @ -310,10 +333,12 @@ func (peer *Peer) RoutineSequentialSender() { | |||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				logger.Println("Sending packet for peer", peer.id, work.packet) | ||||
| 				logger.Println(work.packet) | ||||
| 
 | ||||
| 				_, err := device.net.conn.WriteToUDP(work.packet, peer.endpoint) | ||||
| 				logger.Println("SEND:", peer.endpoint, err) | ||||
| 				if err != nil { | ||||
| 					return | ||||
| 				} | ||||
| 				atomic.AddUint64(&peer.tx_bytes, uint64(len(work.packet))) | ||||
| 
 | ||||
| 				// shift keep-alive timer
 | ||||
|  | @ -323,7 +348,6 @@ func (peer *Peer) RoutineSequentialSender() { | |||
| 					peer.timer.sendKeepalive.Reset(interval) | ||||
| 				} | ||||
| 			}() | ||||
| 			work.mutex.Unlock() | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -4,5 +4,5 @@ type TUNDevice interface { | |||
| 	Read([]byte) (int, error) | ||||
| 	Write([]byte) (int, error) | ||||
| 	Name() string | ||||
| 	MTU() uint | ||||
| 	MTU() int | ||||
| } | ||||
|  |  | |||
|  | @ -24,14 +24,14 @@ const ( | |||
| type NativeTun struct { | ||||
| 	fd   *os.File | ||||
| 	name string | ||||
| 	mtu  uint | ||||
| 	mtu  int | ||||
| } | ||||
| 
 | ||||
| func (tun *NativeTun) Name() string { | ||||
| 	return tun.name | ||||
| } | ||||
| 
 | ||||
| func (tun *NativeTun) MTU() uint { | ||||
| func (tun *NativeTun) MTU() int { | ||||
| 	return tun.mtu | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue