wireguard-go/src/send.go

189 lines
3.2 KiB
Go
Raw Normal View History

2017-06-26 13:14:02 +02:00
package main
import (
"encoding/binary"
"golang.org/x/crypto/chacha20poly1305"
2017-06-26 13:14:02 +02:00
"net"
"sync"
"time"
2017-06-26 13:14:02 +02: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
}
2017-06-27 17:33:06 +02:00
func (peer *Peer) HandshakeWorker(handshakeQueue []byte) {
}
2017-06-26 13:14:02 +02:00
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:
2017-06-27 17:33:06 +02:00
device.log.Debug.Println("receieved packet with unknown IP version")
2017-06-26 13:14:02 +02:00
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
*/
func (peer *Peer) RoutineOutboundNonceWorker() {
var packet []byte
var keyPair *KeyPair
var flushTimer time.Timer
2017-06-26 13:14:02 +02:00
for {
// wait for packet
if packet == nil {
packet = <-peer.queueOutboundRouting
2017-06-26 13:14:02 +02:00
}
// wait for key pair
for keyPair == nil {
flushTimer.Reset(time.Second * 10)
// TODO: Handshake or NOP
2017-06-26 13:14:02 +02:00
select {
case <-peer.keyPairs.newKeyPair:
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 13:14:02 +02:00
// create work element
2017-06-26 13:14:02 +02:00
work := new(OutboundWorkQueueElement)
work.wg.Add(1)
work.keyPair = keyPair
work.packet = packet
work.nonce = keyPair.sendNonce
2017-06-26 13:14:02 +02:00
packet = nil
peer.queueOutbound <- work
keyPair.sendNonce += 1
2017-06-26 13:14:02 +02:00
// drop packets until there is space
2017-06-26 13:14:02 +02:00
func() {
2017-06-26 13:14:02 +02:00
for {
select {
case peer.device.queueWorkOutbound <- work:
return
2017-06-26 13:14:02 +02:00
default:
drop := <-peer.device.queueWorkOutbound
drop.packet = nil
drop.wg.Done()
}
}
}()
2017-06-26 13:14:02 +02:00
}
}
}
/* Go routine
*
* sequentially reads packets from queue and sends to endpoint
*
*/
2017-06-26 13:14:02 +02:00
func (peer *Peer) RoutineSequential() {
for work := range peer.queueOutbound {
work.wg.Wait()
if work.packet == nil {
continue
}
2017-06-27 17:33:06 +02:00
if peer.endpoint == nil {
continue
}
peer.device.conn.WriteToUDP(work.packet, peer.endpoint)
2017-06-26 13:14:02 +02:00
}
}
func (device *Device) RoutineEncryptionWorker() {
var nonce [chacha20poly1305.NonceSize]byte
for work := range device.queueWorkOutbound {
// pad packet
2017-06-26 13:14:02 +02: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 13:14:02 +02:00
// encrypt
2017-06-26 13:14:02 +02: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 13:14:02 +02:00
}
}