Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+297
-149
@@ -8,7 +8,9 @@ package device
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -45,28 +47,26 @@ import (
|
||||
*/
|
||||
|
||||
type QueueOutboundElement struct {
|
||||
buffer *[MaxMessageSize]byte // slice holding the packet data
|
||||
// packet is always a slice of "buffer". The starting offset in buffer
|
||||
// is either:
|
||||
// a) MessageEncapsulatingTransportSize+MessageTransportHeaderSize (plaintext)
|
||||
// b) 0 (post-encryption)
|
||||
packet []byte
|
||||
nonce uint64 // nonce for encryption
|
||||
keypair *Keypair // keypair for encryption
|
||||
peer *Peer // related peer
|
||||
buffer *packetBuf // shared packet buffer that contains packet
|
||||
packet []byte // packet is always a slice of buffer.slab
|
||||
// plaintextOffset describes the pre-encryption offset of packet within
|
||||
// buffer.slab. The encryption process grows packet on both ends to make
|
||||
// room for any potential encapsulating transport, WireGuard header, and
|
||||
// tail padding.
|
||||
plaintextOffset int
|
||||
nonce uint64 // nonce for encryption
|
||||
keypair *Keypair // keypair for encryption
|
||||
peer *Peer // related peer
|
||||
}
|
||||
|
||||
type QueueOutboundElementsContainer struct {
|
||||
sync.Mutex
|
||||
elems []*QueueOutboundElement
|
||||
}
|
||||
|
||||
func (device *Device) NewOutboundElement() *QueueOutboundElement {
|
||||
elem := device.GetOutboundElement()
|
||||
elem.buffer = device.GetMessageBuffer()
|
||||
elem.nonce = 0
|
||||
// keypair and peer were cleared (if necessary) by clearPointers.
|
||||
return elem
|
||||
// filling is a one-shot barrier signaling encryption→send handoff.
|
||||
// SendStagedPackets calls Add(1) before sending the container down
|
||||
// the encryption and outbound queues; RoutineEncryption calls Done
|
||||
// after encrypting; RoutineSequentialSender calls Wait before
|
||||
// reading the encrypted packets.
|
||||
filling sync.WaitGroup
|
||||
elems []*QueueOutboundElement
|
||||
}
|
||||
|
||||
// clearPointers clears elem fields that contain pointers.
|
||||
@@ -80,18 +80,24 @@ func (elem *QueueOutboundElement) clearPointers() {
|
||||
elem.peer = nil
|
||||
}
|
||||
|
||||
/* Queues a keepalive if no packets are queued for peer
|
||||
*/
|
||||
// SendKeepalive queues a keepalive if no packets are queued for
|
||||
// peer.
|
||||
func (peer *Peer) SendKeepalive() {
|
||||
if len(peer.queue.staged) == 0 && peer.isRunning.Load() {
|
||||
elem := peer.device.NewOutboundElement()
|
||||
if len(peer.queue.staged) == 0 {
|
||||
elem := peer.device.GetOutboundElement()
|
||||
elem.buffer = peer.device.getSmallPacketBuf()
|
||||
elem.plaintextOffset = MessageEncapsulatingTransportSize + MessageTransportHeaderSize
|
||||
elemsContainer := peer.device.GetOutboundElementsContainer()
|
||||
elemsContainer.elems = append(elemsContainer.elems, elem)
|
||||
select {
|
||||
case peer.queue.staged <- elemsContainer:
|
||||
peer.device.log.Verbosef("%v - Sending keepalive packet", peer)
|
||||
default:
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
if queued := peer.doIfRunning(func() {
|
||||
select {
|
||||
case peer.queue.staged <- elemsContainer:
|
||||
peer.device.log.Verbosef("%v - Sending keepalive packet", peer)
|
||||
default:
|
||||
peer.device.PutOutboundElement(elem)
|
||||
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||
}
|
||||
}); !queued {
|
||||
peer.device.PutOutboundElement(elem)
|
||||
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||
}
|
||||
@@ -99,6 +105,62 @@ func (peer *Peer) SendKeepalive() {
|
||||
peer.SendStagedPackets()
|
||||
}
|
||||
|
||||
// SendPriorityMessage invokes the [PeerPriorityMessageFunc] callback if one is
|
||||
// set, and queues the returned message for encryption and transmission if the
|
||||
// current keypair is valid.
|
||||
func (peer *Peer) SendPriorityMessage() {
|
||||
f := peer.device.priorityMsgFn.Load()
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
keypair := peer.keypairs.Current()
|
||||
if keypair == nil || keypair.sendNonce.Load() >= RejectAfterMessages || time.Since(keypair.created) >= RejectAfterTime {
|
||||
// SendStagedPackets initializes a handshake when the keypair is invalid,
|
||||
// but we explicitly avoid that here. A priority message is only intended
|
||||
// to flow around symmetric session establishment, but it should never
|
||||
// trigger a new session. Reaching this branch due to nonce exhaustion
|
||||
// or keypair expiration is highly unlikely considering where
|
||||
// SendPriorityMessage is called (at current keypair establishment).
|
||||
return
|
||||
}
|
||||
|
||||
// get plaintext message to send
|
||||
msg := (*f)(peer.handshake.remoteStatic)
|
||||
if len(msg) == 0 {
|
||||
return
|
||||
}
|
||||
if len(msg) > MaxPriorityMessageContentSize {
|
||||
peer.device.log.Verbosef("%v - Failed to queue priority message due to size", peer)
|
||||
return
|
||||
}
|
||||
|
||||
// get pooled elements
|
||||
elem := peer.device.GetOutboundElement()
|
||||
elemsContainer := peer.device.GetOutboundElementsContainer()
|
||||
elemsContainer.elems = append(elemsContainer.elems, elem)
|
||||
buf := peer.device.getSmallPacketBuf()
|
||||
|
||||
// initialize outbound element
|
||||
const offset = MessageEncapsulatingTransportSize + MessageTransportHeaderSize
|
||||
|
||||
n := copy(buf.slab[offset:], msg)
|
||||
elem.buffer = buf
|
||||
elem.packet = buf.slab[offset : offset+n]
|
||||
elem.plaintextOffset = offset
|
||||
elem.peer = peer
|
||||
elem.nonce = keypair.sendNonce.Add(1) - 1
|
||||
if elem.nonce >= RejectAfterMessages {
|
||||
keypair.sendNonce.Store(RejectAfterMessages)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||
return
|
||||
}
|
||||
elem.keypair = keypair
|
||||
|
||||
// add to parallel and sequential queue
|
||||
peer.queueOutboundIfRunning(elemsContainer)
|
||||
}
|
||||
|
||||
func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
if !isRetry {
|
||||
peer.timers.handshakeAttempts.Store(0)
|
||||
@@ -116,6 +178,10 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
peer.handshake.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
// handshakeOnUserSend is flipped false after lastSentHandshake checks,
|
||||
// enabling eventual transmission at a future call of this method, while
|
||||
// still honoring [RekeyTimeout].
|
||||
peer.handshakeOnUserSend.Store(false)
|
||||
peer.handshake.lastSentHandshake = time.Now()
|
||||
peer.handshake.mutex.Unlock()
|
||||
|
||||
@@ -135,6 +201,11 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketSent()
|
||||
|
||||
if isRetry {
|
||||
peer.device.config.metrics.MessageInitiationTXAttemptRetry.Add(1)
|
||||
} else {
|
||||
peer.device.config.metrics.MessageInitiationTXAttemptInitial.Add(1)
|
||||
}
|
||||
err = peer.SendBuffers([][]byte{buf})
|
||||
if err != nil {
|
||||
peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err)
|
||||
@@ -172,6 +243,7 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketSent()
|
||||
|
||||
peer.device.config.metrics.MessageResponseTXAttempt.Add(1)
|
||||
// TODO: allocation could be avoided
|
||||
err = peer.SendBuffers([][]byte{buf})
|
||||
if err != nil {
|
||||
@@ -181,10 +253,10 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
}
|
||||
|
||||
func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement) error {
|
||||
device.log.Verbosef("Sending cookie response for denied handshake message for %v", initiatingElem.endpoint.DstToString())
|
||||
device.log.Verbosef("Sending cookie response for denied handshake message for %v", initiatingElem.packetMeta.Endpoint.DstToString())
|
||||
|
||||
sender := binary.LittleEndian.Uint32(initiatingElem.packet[4:8])
|
||||
reply, err := device.cookieChecker.CreateReply(initiatingElem.packet, sender, initiatingElem.endpoint.DstToBytes())
|
||||
reply, err := device.cookieChecker.CreateReply(initiatingElem.packet, sender, initiatingElem.packetMeta.Endpoint.DstToBytes())
|
||||
if err != nil {
|
||||
device.log.Errorf("Failed to create cookie reply: %v", err)
|
||||
return err
|
||||
@@ -193,8 +265,9 @@ func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement)
|
||||
buf := make([]byte, MessageEncapsulatingTransportSize+MessageCookieReplySize)
|
||||
packet := buf[MessageEncapsulatingTransportSize:]
|
||||
_ = reply.marshal(packet)
|
||||
device.config.metrics.MessageCookieReplyTXAttempt.Add(1)
|
||||
// TODO: allocation could be avoided
|
||||
device.net.bind.Send([][]byte{buf}, initiatingElem.endpoint, MessageEncapsulatingTransportSize)
|
||||
device.net.bind.Send([][]byte{buf}, initiatingElem.packetMeta.Endpoint, MessageEncapsulatingTransportSize)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -204,73 +277,132 @@ func (peer *Peer) keepKeyFreshSending() {
|
||||
if keypair == nil {
|
||||
return
|
||||
}
|
||||
txHandshake := peer.handshakeOnUserSend.Load()
|
||||
nonce := keypair.sendNonce.Load()
|
||||
if nonce > RekeyAfterMessages || (keypair.isInitiator && time.Since(keypair.created) > RekeyAfterTime) {
|
||||
if txHandshake || nonce > RekeyAfterMessages || (keypair.isInitiator && time.Since(keypair.created) > RekeyAfterTime) {
|
||||
peer.SendHandshakeInitiation(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) RoutineReadFromTUN() {
|
||||
// copyPacketBufIfFits copies the prefix of src.slab ending at the final packet
|
||||
// into dst.slab if that prefix+[outboundPlaintextTailroom] fits. It reports
|
||||
// whether the copy occurred. Packet descriptors must be in offset ascending order.
|
||||
func copyPacketBufIfFits(dst, src *packetBuf, packets []tun.ReadPacket) bool {
|
||||
if dst == nil || len(packets) < 1 {
|
||||
return false
|
||||
}
|
||||
// validate tail descriptor as it relates to src
|
||||
tailPkt := packets[len(packets)-1]
|
||||
if tailPkt.Offset < 0 || tailPkt.Size < 1 {
|
||||
return false
|
||||
}
|
||||
end := tailPkt.Offset + tailPkt.Size
|
||||
if end < tailPkt.Offset || end > len(src.slab) ||
|
||||
outboundPlaintextTailroom > len(src.slab)-end {
|
||||
return false
|
||||
}
|
||||
// validate src fits in dst
|
||||
if end+outboundPlaintextTailroom > len(dst.slab) {
|
||||
return false
|
||||
}
|
||||
copy(dst.slab, src.slab[:end])
|
||||
return true
|
||||
}
|
||||
|
||||
func (device *Device) RoutineReadFromTUN(id int, queue tun.Reader) {
|
||||
defer func() {
|
||||
device.log.Verbosef("Routine: TUN reader - stopped")
|
||||
device.log.Verbosef("Routine: TUN reader %d - stopped", id)
|
||||
device.state.stopping.Done()
|
||||
device.queue.encryption.wg.Done()
|
||||
}()
|
||||
|
||||
device.log.Verbosef("Routine: TUN reader - started")
|
||||
device.log.Verbosef("Routine: TUN reader %d - started", id)
|
||||
|
||||
var (
|
||||
batchSize = device.BatchSize()
|
||||
packets = make([]tun.ReadPacket, batchSize)
|
||||
fullSizeBuf = device.getPacketBuf()
|
||||
smallBuf *packetBuf
|
||||
readErr error
|
||||
elems = make([]*QueueOutboundElement, batchSize)
|
||||
bufs = make([][]byte, batchSize)
|
||||
elemsByPeer = make(map[*Peer]*QueueOutboundElementsContainer, batchSize)
|
||||
count = 0
|
||||
sizes = make([]int, batchSize)
|
||||
offset = MessageEncapsulatingTransportSize + MessageTransportHeaderSize
|
||||
)
|
||||
|
||||
if device.hasDistinctSmallPacketBufPool() {
|
||||
smallBuf = device.getSmallPacketBuf()
|
||||
}
|
||||
|
||||
defer func() {
|
||||
fullSizeBuf.decRef()
|
||||
if smallBuf != nil {
|
||||
smallBuf.decRef()
|
||||
}
|
||||
}()
|
||||
|
||||
for i := range elems {
|
||||
elems[i] = device.NewOutboundElement()
|
||||
bufs[i] = elems[i].buffer[:]
|
||||
elems[i] = device.GetOutboundElement()
|
||||
}
|
||||
|
||||
defer func() {
|
||||
for _, elem := range elems {
|
||||
if elem != nil {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
buf *packetBuf // either fullSizeBuf or smallBuf
|
||||
bufHandedOff bool
|
||||
)
|
||||
|
||||
for {
|
||||
if bufHandedOff {
|
||||
if buf == smallBuf {
|
||||
smallBuf.decRef()
|
||||
smallBuf = device.getSmallPacketBuf()
|
||||
} else {
|
||||
fullSizeBuf.decRef()
|
||||
fullSizeBuf = device.getPacketBuf()
|
||||
}
|
||||
}
|
||||
buf = fullSizeBuf
|
||||
bufHandedOff = false
|
||||
|
||||
// read packets
|
||||
count, readErr = device.tun.device.Read(bufs, sizes, offset)
|
||||
for i := 0; i < count; i++ {
|
||||
if sizes[i] < 1 {
|
||||
count, readErr = queue.Read(fullSizeBuf.slab, packets)
|
||||
if copyPacketBufIfFits(smallBuf, fullSizeBuf, packets[:count]) {
|
||||
buf = smallBuf
|
||||
}
|
||||
|
||||
for i, meta := range packets[:count] {
|
||||
if meta.Size < 1 || meta.Size > MaxContentSize {
|
||||
continue
|
||||
}
|
||||
|
||||
elem := elems[i]
|
||||
elem.packet = bufs[i][offset : offset+sizes[i]]
|
||||
// explicitly pin slice capacity, so downstream expansion of length
|
||||
// never leak into an adjacent packet
|
||||
packet := buf.slab[meta.Offset : meta.Offset+meta.Size : meta.Offset+meta.Size+outboundPlaintextTailroom]
|
||||
|
||||
// lookup peer
|
||||
var peer *Peer
|
||||
switch elem.packet[0] >> 4 {
|
||||
switch packet[0] >> 4 {
|
||||
case 4:
|
||||
if len(elem.packet) < ipv4.HeaderLen {
|
||||
if len(packet) < ipv4.HeaderLen {
|
||||
continue
|
||||
}
|
||||
dst := elem.packet[IPv4offsetDst : IPv4offsetDst+net.IPv4len]
|
||||
peer = device.allowedips.Lookup(dst)
|
||||
src := netip.AddrFrom4([4]byte(packet[IPv4offsetSrc : IPv4offsetSrc+net.IPv4len]))
|
||||
dst := netip.AddrFrom4([4]byte(packet[IPv4offsetDst : IPv4offsetDst+net.IPv4len]))
|
||||
peer = device.allowedips.LookupFromPacket(src, dst, packet)
|
||||
|
||||
case 6:
|
||||
if len(elem.packet) < ipv6.HeaderLen {
|
||||
if len(packet) < ipv6.HeaderLen {
|
||||
continue
|
||||
}
|
||||
dst := elem.packet[IPv6offsetDst : IPv6offsetDst+net.IPv6len]
|
||||
peer = device.allowedips.Lookup(dst)
|
||||
src := netip.AddrFrom16([16]byte(packet[IPv6offsetSrc : IPv6offsetSrc+net.IPv6len]))
|
||||
dst := netip.AddrFrom16([16]byte(packet[IPv6offsetDst : IPv6offsetDst+net.IPv6len]))
|
||||
peer = device.allowedips.LookupFromPacket(src, dst, packet)
|
||||
|
||||
default:
|
||||
device.log.Verbosef("Received packet with unknown IP version")
|
||||
@@ -279,27 +411,26 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
if peer == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
elem := elems[i]
|
||||
buf.incRef()
|
||||
bufHandedOff = true
|
||||
elem.buffer = buf
|
||||
elem.packet = packet
|
||||
elem.plaintextOffset = meta.Offset
|
||||
|
||||
elemsForPeer, ok := elemsByPeer[peer]
|
||||
if !ok {
|
||||
elemsForPeer = device.GetOutboundElementsContainer()
|
||||
elemsByPeer[peer] = elemsForPeer
|
||||
}
|
||||
elemsForPeer.elems = append(elemsForPeer.elems, elem)
|
||||
elems[i] = device.NewOutboundElement()
|
||||
bufs[i] = elems[i].buffer[:]
|
||||
elems[i] = device.GetOutboundElement()
|
||||
}
|
||||
|
||||
for peer, elemsForPeer := range elemsByPeer {
|
||||
if peer.isRunning.Load() {
|
||||
peer.StagePackets(elemsForPeer)
|
||||
peer.SendStagedPackets()
|
||||
} else {
|
||||
for _, elem := range elemsForPeer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
device.PutOutboundElementsContainer(elemsForPeer)
|
||||
}
|
||||
peer.StagePackets(elemsForPeer)
|
||||
peer.SendStagedPackets()
|
||||
delete(elemsByPeer, peer)
|
||||
}
|
||||
|
||||
@@ -323,24 +454,31 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
}
|
||||
|
||||
func (peer *Peer) StagePackets(elems *QueueOutboundElementsContainer) {
|
||||
for {
|
||||
select {
|
||||
case peer.queue.staged <- elems:
|
||||
return
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case tooOld := <-peer.queue.staged:
|
||||
for _, elem := range tooOld.elems {
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
if running := peer.doIfRunning(func() {
|
||||
for {
|
||||
select {
|
||||
case peer.queue.staged <- elems:
|
||||
return
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case tooOld := <-peer.queue.staged:
|
||||
for _, elem := range tooOld.elems {
|
||||
peer.device.PutOutboundElement(elem)
|
||||
}
|
||||
peer.device.PutOutboundElementsContainer(tooOld)
|
||||
default:
|
||||
}
|
||||
peer.device.PutOutboundElementsContainer(tooOld)
|
||||
default:
|
||||
}
|
||||
}); !running {
|
||||
for _, elem := range elems.elems {
|
||||
peer.device.PutOutboundElement(elem)
|
||||
}
|
||||
peer.device.PutOutboundElementsContainer(elems)
|
||||
}
|
||||
}
|
||||
|
||||
// SendStagedPackets sends any staged packets to Peer.
|
||||
func (peer *Peer) SendStagedPackets() {
|
||||
top:
|
||||
if len(peer.queue.staged) == 0 || !peer.device.isUp() {
|
||||
@@ -375,7 +513,6 @@ top:
|
||||
|
||||
elem.keypair = keypair
|
||||
}
|
||||
elemsContainer.Lock()
|
||||
elemsContainer.elems = elemsContainer.elems[:i]
|
||||
|
||||
if elemsContainerOOO != nil {
|
||||
@@ -387,17 +524,7 @@ top:
|
||||
goto top
|
||||
}
|
||||
|
||||
// add to parallel and sequential queue
|
||||
if peer.isRunning.Load() {
|
||||
peer.queue.outbound.c <- elemsContainer
|
||||
peer.device.queue.encryption.c <- elemsContainer
|
||||
} else {
|
||||
for _, elem := range elemsContainer.elems {
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
}
|
||||
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||
}
|
||||
peer.queueOutboundIfRunning(elemsContainer)
|
||||
|
||||
if elemsContainerOOO != nil {
|
||||
goto top
|
||||
@@ -413,7 +540,6 @@ func (peer *Peer) FlushStagedPackets() {
|
||||
select {
|
||||
case elemsContainer := <-peer.queue.staged:
|
||||
for _, elem := range elemsContainer.elems {
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
}
|
||||
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||
@@ -453,7 +579,7 @@ func (device *Device) RoutineEncryption(id int) {
|
||||
for elemsContainer := range device.queue.encryption.c {
|
||||
for _, elem := range elemsContainer.elems {
|
||||
// populate header fields
|
||||
header := elem.buffer[MessageEncapsulatingTransportSize : MessageEncapsulatingTransportSize+MessageTransportHeaderSize]
|
||||
header := elem.buffer.slab[elem.plaintextOffset-MessageTransportHeaderSize : elem.plaintextOffset]
|
||||
|
||||
fieldType := header[0:4]
|
||||
fieldReceiver := header[4:8]
|
||||
@@ -465,6 +591,7 @@ func (device *Device) RoutineEncryption(id int) {
|
||||
|
||||
// pad content to multiple of 16
|
||||
paddingSize := calculatePaddingSize(len(elem.packet), int(device.tun.mtu.Load()))
|
||||
|
||||
elem.packet = append(elem.packet, paddingZeros[:paddingSize]...)
|
||||
|
||||
// encrypt content and release to consumer
|
||||
@@ -478,9 +605,11 @@ func (device *Device) RoutineEncryption(id int) {
|
||||
)
|
||||
|
||||
// re-slice packet to include encapsulating transport space
|
||||
elem.packet = elem.buffer[:MessageEncapsulatingTransportSize+len(elem.packet)]
|
||||
start := elem.plaintextOffset - MessageTransportHeaderSize - MessageEncapsulatingTransportSize
|
||||
end := elem.plaintextOffset - MessageTransportHeaderSize + len(elem.packet)
|
||||
elem.packet = elem.buffer.slab[start:end]
|
||||
}
|
||||
elemsContainer.Unlock()
|
||||
elemsContainer.filling.Done()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,65 +617,84 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
|
||||
device := peer.device
|
||||
defer func() {
|
||||
defer device.log.Verbosef("%v - Routine: sequential sender - stopped", peer)
|
||||
peer.stopping.Done()
|
||||
peer.runningState.queueReaders.Done()
|
||||
}()
|
||||
device.log.Verbosef("%v - Routine: sequential sender - started", peer)
|
||||
|
||||
bufs := make([][]byte, 0, maxBatchSize)
|
||||
|
||||
for elemsContainer := range peer.queue.outbound.c {
|
||||
bufs = bufs[:0]
|
||||
if elemsContainer == nil {
|
||||
return
|
||||
}
|
||||
if !peer.isRunning.Load() {
|
||||
// peer has been stopped; return re-usable elems to the shared pool.
|
||||
// This is an optimization only. It is possible for the peer to be stopped
|
||||
// immediately after this check, in which case, elem will get processed.
|
||||
// The timers and SendBuffers code are resilient to a few stragglers.
|
||||
// TODO: rework peer shutdown order to ensure
|
||||
// that we never accidentally keep timers alive longer than necessary.
|
||||
elemsContainer.Lock()
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
device.PutOutboundElementsContainer(elemsContainer)
|
||||
continue
|
||||
}
|
||||
dataSent := false
|
||||
elemsContainer.Lock()
|
||||
for _, elem := range elemsContainer.elems {
|
||||
if len(elem.packet[MessageEncapsulatingTransportSize:]) != MessageKeepaliveSize {
|
||||
dataSent = true
|
||||
}
|
||||
bufs = append(bufs, elem.packet)
|
||||
}
|
||||
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketSent()
|
||||
|
||||
err := peer.SendBuffers(bufs)
|
||||
if dataSent {
|
||||
peer.timersDataSent()
|
||||
}
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
device.PutOutboundElementsContainer(elemsContainer)
|
||||
if err != nil {
|
||||
var errGSO conn.ErrUDPGSODisabled
|
||||
if errors.As(err, &errGSO) {
|
||||
device.log.Verbosef(err.Error())
|
||||
err = errGSO.RetryErr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
device.log.Errorf("%v - Failed to send data packets: %v", peer, err)
|
||||
continue
|
||||
}
|
||||
|
||||
peer.keepKeyFreshSending()
|
||||
for elemsContainer := range peer.queue.outbound {
|
||||
peer.processOutboundContainer(elemsContainer, bufs[:0])
|
||||
}
|
||||
}
|
||||
|
||||
// processOutboundContainer waits for the encryption routine to finish
|
||||
// filling elemsContainer, then sends the batch (or drops it, if the peer
|
||||
// has been stopped) and returns the container to the pool.
|
||||
//
|
||||
// scratch is a length-0 slice used to assemble the per-packet buffers
|
||||
// passed to SendBuffers; its backing array is reused across calls.
|
||||
func (peer *Peer) processOutboundContainer(elemsContainer *QueueOutboundElementsContainer, scratch [][]byte) {
|
||||
// Invariants from RoutineSequentialSender; all should be unreachable.
|
||||
if len(scratch) != 0 || cap(scratch) == 0 {
|
||||
panic(fmt.Sprintf("processOutboundContainer: scratch must be empty with non-zero cap; got len=%d cap=%d",
|
||||
len(scratch), cap(scratch)))
|
||||
}
|
||||
if cap(scratch) < len(elemsContainer.elems) {
|
||||
panic(fmt.Sprintf("processOutboundContainer: scratch cap %d < elems %d",
|
||||
cap(scratch), len(elemsContainer.elems)))
|
||||
}
|
||||
|
||||
device := peer.device
|
||||
defer device.PutOutboundElementsContainer(elemsContainer)
|
||||
|
||||
// Wait for RoutineEncryption to finish filling the container. After
|
||||
// Wait returns we have happens-before with that goroutine and are the
|
||||
// sole owner of the container until Put hands it back to the pool.
|
||||
elemsContainer.filling.Wait()
|
||||
|
||||
if !peer.runningState.isRunning.Load() {
|
||||
// peer has been stopped; return re-usable elems to the shared pool.
|
||||
// This is an optimization only. It is possible for the peer to be stopped
|
||||
// immediately after this check, in which case, elem will get processed.
|
||||
// The timers and SendBuffers code are resilient to a few stragglers.
|
||||
// TODO: rework peer shutdown order to ensure
|
||||
// that we never accidentally keep timers alive longer than necessary.
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
dataSent := false
|
||||
for _, elem := range elemsContainer.elems {
|
||||
if len(elem.packet[MessageEncapsulatingTransportSize:]) != MessageKeepaliveSize {
|
||||
dataSent = true
|
||||
}
|
||||
scratch = append(scratch, elem.packet)
|
||||
}
|
||||
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketSent()
|
||||
|
||||
err := peer.SendBuffers(scratch)
|
||||
if dataSent {
|
||||
peer.timersDataSent()
|
||||
}
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
if err != nil {
|
||||
var errGSO conn.ErrUDPGSODisabled
|
||||
if errors.As(err, &errGSO) {
|
||||
device.log.Verbosef(err.Error())
|
||||
err = errGSO.RetryErr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
device.log.Errorf("%v - Failed to send data packets: %v", peer, err)
|
||||
return
|
||||
}
|
||||
|
||||
peer.keepKeyFreshSending()
|
||||
}
|
||||
Reference in new issue
Block a user