2017-06-26 11:14:02 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2017-06-26 20:07:29 +00:00
|
|
|
"encoding/binary"
|
|
|
|
"golang.org/x/crypto/chacha20poly1305"
|
2017-06-26 11:14:02 +00:00
|
|
|
"net"
|
|
|
|
"sync"
|
2017-06-26 20:07:29 +00:00
|
|
|
"time"
|
2017-06-26 11:14:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
/* Handles outbound flow
|
|
|
|
*
|
|
|
|
* 1. TUN queue
|
|
|
|
* 2. Routing
|
|
|
|
* 3. Per peer queuing
|
|
|
|
* 4. (work queuing)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
type OutboundWorkQueueElement struct {
|
|
|
|
wg sync.WaitGroup
|
|
|
|
packet []byte
|
|
|
|
nonce uint64
|
|
|
|
keyPair *KeyPair
|
|
|
|
}
|
|
|
|
|
|
|
|
func (device *Device) SendPacket(packet []byte) {
|
|
|
|
|
|
|
|
// lookup peer
|
|
|
|
|
|
|
|
var peer *Peer
|
|
|
|
switch packet[0] >> 4 {
|
|
|
|
case IPv4version:
|
|
|
|
dst := packet[IPv4offsetDst : IPv4offsetDst+net.IPv4len]
|
|
|
|
peer = device.routingTable.LookupIPv4(dst)
|
|
|
|
|
|
|
|
case IPv6version:
|
|
|
|
dst := packet[IPv6offsetDst : IPv6offsetDst+net.IPv6len]
|
|
|
|
peer = device.routingTable.LookupIPv6(dst)
|
|
|
|
|
|
|
|
default:
|
|
|
|
device.logger.Println("unknown IP version")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if peer == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert into peer queue
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case peer.queueOutboundRouting <- packet:
|
|
|
|
default:
|
|
|
|
select {
|
|
|
|
case <-peer.queueOutboundRouting:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Go routine
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* 1. waits for handshake.
|
|
|
|
* 2. assigns key pair & nonce
|
|
|
|
* 3. inserts to working queue
|
|
|
|
*
|
|
|
|
* TODO: avoid dynamic allocation of work queue elements
|
|
|
|
*/
|
2017-06-26 20:07:29 +00:00
|
|
|
func (peer *Peer) RoutineOutboundNonceWorker() {
|
|
|
|
var packet []byte
|
|
|
|
var keyPair *KeyPair
|
|
|
|
var flushTimer time.Timer
|
|
|
|
|
2017-06-26 11:14:02 +00:00
|
|
|
for {
|
2017-06-26 20:07:29 +00:00
|
|
|
|
|
|
|
// wait for packet
|
|
|
|
|
|
|
|
if packet == nil {
|
|
|
|
packet = <-peer.queueOutboundRouting
|
2017-06-26 11:14:02 +00:00
|
|
|
}
|
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
// wait for key pair
|
|
|
|
|
|
|
|
for keyPair == nil {
|
|
|
|
flushTimer.Reset(time.Second * 10)
|
|
|
|
// TODO: Handshake or NOP
|
2017-06-26 11:14:02 +00:00
|
|
|
select {
|
|
|
|
case <-peer.keyPairs.newKeyPair:
|
2017-06-26 20:07:29 +00:00
|
|
|
keyPair = peer.keyPairs.Current()
|
|
|
|
continue
|
|
|
|
case <-flushTimer.C:
|
|
|
|
size := len(peer.queueOutboundRouting)
|
|
|
|
for i := 0; i < size; i += 1 {
|
|
|
|
<-peer.queueOutboundRouting
|
|
|
|
}
|
|
|
|
packet = nil
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// process current packet
|
|
|
|
|
|
|
|
if packet != nil {
|
2017-06-26 11:14:02 +00:00
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
// create work element
|
2017-06-26 11:14:02 +00:00
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
work := new(OutboundWorkQueueElement)
|
|
|
|
work.wg.Add(1)
|
|
|
|
work.keyPair = keyPair
|
|
|
|
work.packet = packet
|
|
|
|
work.nonce = keyPair.sendNonce
|
2017-06-26 11:14:02 +00:00
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
packet = nil
|
|
|
|
peer.queueOutbound <- work
|
|
|
|
keyPair.sendNonce += 1
|
2017-06-26 11:14:02 +00:00
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
// drop packets until there is space
|
2017-06-26 11:14:02 +00:00
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
func() {
|
2017-06-26 11:14:02 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case peer.device.queueWorkOutbound <- work:
|
2017-06-26 20:07:29 +00:00
|
|
|
return
|
2017-06-26 11:14:02 +00:00
|
|
|
default:
|
|
|
|
drop := <-peer.device.queueWorkOutbound
|
|
|
|
drop.packet = nil
|
|
|
|
drop.wg.Done()
|
|
|
|
}
|
|
|
|
}
|
2017-06-26 20:07:29 +00:00
|
|
|
}()
|
2017-06-26 11:14:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
/* Go routine
|
|
|
|
*
|
|
|
|
* sequentially reads packets from queue and sends to endpoint
|
|
|
|
*
|
|
|
|
*/
|
2017-06-26 11:14:02 +00:00
|
|
|
func (peer *Peer) RoutineSequential() {
|
|
|
|
for work := range peer.queueOutbound {
|
|
|
|
work.wg.Wait()
|
2017-06-26 20:07:29 +00:00
|
|
|
|
|
|
|
// check if dropped ("ghost packet")
|
|
|
|
|
2017-06-26 11:14:02 +00:00
|
|
|
if work.packet == nil {
|
|
|
|
continue
|
|
|
|
}
|
2017-06-26 20:07:29 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
|
2017-06-26 11:14:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
func (device *Device) RoutineEncryptionWorker() {
|
|
|
|
var nonce [chacha20poly1305.NonceSize]byte
|
|
|
|
for work := range device.queueWorkOutbound {
|
|
|
|
// pad packet
|
2017-06-26 11:14:02 +00:00
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
padding := device.mtu - len(work.packet)
|
|
|
|
if padding < 0 {
|
|
|
|
work.packet = nil
|
|
|
|
work.wg.Done()
|
|
|
|
}
|
|
|
|
for n := 0; n < padding; n += 1 {
|
|
|
|
work.packet = append(work.packet, 0)
|
|
|
|
}
|
2017-06-26 11:14:02 +00:00
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
// encrypt
|
2017-06-26 11:14:02 +00:00
|
|
|
|
2017-06-26 20:07:29 +00:00
|
|
|
binary.LittleEndian.PutUint64(nonce[4:], work.nonce)
|
|
|
|
work.packet = work.keyPair.send.Seal(
|
|
|
|
work.packet[:0],
|
|
|
|
nonce[:],
|
|
|
|
work.packet,
|
|
|
|
nil,
|
|
|
|
)
|
|
|
|
work.wg.Done()
|
2017-06-26 11:14:02 +00:00
|
|
|
}
|
|
|
|
}
|