Files
weave/router/sleeve.go
T
Murali Reddy 3c67c3c5f1 changes differentiate beteween fatal errors (for e.g. ipsec init)
from transient errors (heartbeast misses). In case of transient
errors OverlayForwarder is marked to be unhealty. When overlay forwarder
is unhealty its hept on hold so chooseBest() will skip selecting the forwrder

Once OverlayForwarder is considered healthy run chooseBest() again so the
forwarder can be selected if its the best forwarder

Fixes #1737
2018-09-21 16:45:47 +05:30

1176 lines
31 KiB
Go

// This contains the Overlay implementation for weave's own UDP
// encapsulation protocol ("sleeve" because a sleeve encapsulates
// something, it's often woven, it rhymes with "weave", make up your
// own cheesy reason).
package router
import (
"encoding/binary"
"fmt"
"io"
"net"
"os"
"sync"
"syscall"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/weaveworks/mesh"
)
// This diagram explains the various arithmetic and variables related
// to packet offsets and lengths below:
//
// +----+-----+--------+--------+----------+--------------------------+
// | IP | UDP | Sleeve | Sleeve | Overlay | Overlay Layer 3 Payload |
// | | | Packet | Frame | Ethernet | |
// | | | Header | Header | | |
// +----+-----+--------+--------+----------+--------------------------+
//
// <------------------------------------ msgTooBigError.underlayPMTU ->
//
// <-------------------------- sleeveForwarder.maxPayload ->
//
// <----------> UDPOverhead
//
// <--------> Encryptor.PacketOverhead
//
// <--------> Encryptor.FrameOverhead
//
// <----------> EthernetOverhead
//
// <---------------------------------------> sleeveForwarder.overheadDF
//
// sleeveForwarder.mtu <-------------------------->
const (
EthernetOverhead = 14
UDPOverhead = 28 // 20 bytes for IPv4, 8 bytes for UDP
DefaultMTU = 65535
FragTestSize = 60001
PMTUDiscoverySize = 60000
FragTestInterval = 5 * time.Minute
MTUVerifyAttempts = 8
MTUVerifyTimeout = 10 * time.Millisecond // doubled with each attempt
ProtocolConnectionEstablished = mesh.ProtocolReserved1
ProtocolFragmentationReceived = mesh.ProtocolReserved2
ProtocolPMTUVerified = mesh.ProtocolReserved3
)
type SleeveOverlay struct {
host string
localPort int
// These fields are set in StartConsumingPackets, and not
// subsequently modified
localPeer *mesh.Peer
localPeerBin []byte
consumer OverlayConsumer
peers *mesh.Peers
conn *net.UDPConn
lock sync.Mutex
forwarders map[mesh.PeerName]*sleeveForwarder
}
func NewSleeveOverlay(host string, localPort int) NetworkOverlay {
return &SleeveOverlay{host: host, localPort: localPort}
}
func (sleeve *SleeveOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
localAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprint(sleeve.host, ":", sleeve.localPort))
if err != nil {
return err
}
conn, err := net.ListenUDP("udp4", localAddr)
if err != nil {
return err
}
f, err := conn.File()
if err != nil {
return err
}
defer f.Close()
fd := int(f.Fd())
// This makes sure all packets we send out do not have DF set
// on them.
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DONT)
if err != nil {
return err
}
sleeve.lock.Lock()
defer sleeve.lock.Unlock()
if sleeve.localPeer != nil {
conn.Close()
return fmt.Errorf("StartConsumingPackets already called")
}
sleeve.localPeer = localPeer
sleeve.localPeerBin = localPeer.NameByte
sleeve.consumer = consumer
sleeve.peers = peers
sleeve.conn = conn
sleeve.forwarders = make(map[mesh.PeerName]*sleeveForwarder)
go sleeve.readUDP()
return nil
}
func (*SleeveOverlay) InvalidateRoutes() {
// no cached information, so nothing to do
}
func (*SleeveOverlay) InvalidateShortIDs() {
// no cached information, so nothing to do
}
func (*SleeveOverlay) AddFeaturesTo(map[string]string) {
// No features to be provided, to facilitate compatibility
}
func (*SleeveOverlay) Diagnostics() interface{} {
return nil
}
func (*SleeveOverlay) Stop() {
// do nothing
}
func (sleeve *SleeveOverlay) lookupForwarder(peer mesh.PeerName) *sleeveForwarder {
sleeve.lock.Lock()
defer sleeve.lock.Unlock()
return sleeve.forwarders[peer]
}
func (sleeve *SleeveOverlay) addForwarder(peer mesh.PeerName, fwd *sleeveForwarder) {
sleeve.lock.Lock()
defer sleeve.lock.Unlock()
sleeve.forwarders[peer] = fwd
}
func (sleeve *SleeveOverlay) removeForwarder(peer mesh.PeerName, fwd *sleeveForwarder) {
sleeve.lock.Lock()
defer sleeve.lock.Unlock()
if sleeve.forwarders[peer] == fwd {
delete(sleeve.forwarders, peer)
}
}
func (sleeve *SleeveOverlay) readUDP() {
defer sleeve.conn.Close()
dec := NewEthernetDecoder()
buf := make([]byte, MaxUDPPacketSize)
for {
n, sender, err := sleeve.conn.ReadFromUDP(buf)
if err == io.EOF {
return
} else if err != nil {
log.Print("ignoring UDP read error ", err)
continue
} else if n < NameSize {
log.Print("ignoring too short UDP packet from ", sender)
continue
}
fwdName := mesh.PeerNameFromBin(buf[:NameSize])
fwd := sleeve.lookupForwarder(fwdName)
if fwd == nil {
continue
}
packet := make([]byte, n-NameSize)
copy(packet, buf[NameSize:n])
err = fwd.crypto.Dec.IterateFrames(packet,
func(src []byte, dst []byte, frame []byte) {
sleeve.handleFrame(sender, fwd, src, dst, frame, dec)
})
if err != nil {
// Errors during UDP packet decoding /
// processing are non-fatal. One common cause
// is that we receive and attempt to decrypt a
// "stray" packet. This can actually happen
// quite easily if there is some connection
// churn between two peers. After all, UDP
// isn't a connection-oriented protocol, yet
// we pretend it is.
//
// If anything really is seriously,
// unrecoverably amiss with a connection, that
// will typically result in missed heartbeats
// and the connection getting shut down
// because of that.
log.Print(fwd.logPrefixFor(sender), err)
}
}
}
func (sleeve *SleeveOverlay) handleFrame(sender *net.UDPAddr, fwd *sleeveForwarder, src []byte, dst []byte, frame []byte, dec *EthernetDecoder) {
dec.DecodeLayers(frame)
decodedLen := len(dec.decoded)
if decodedLen == 0 {
return
}
srcPeer := sleeve.peers.Fetch(mesh.PeerNameFromBin(src))
dstPeer := sleeve.peers.Fetch(mesh.PeerNameFromBin(dst))
if srcPeer == nil || dstPeer == nil {
return
}
// Handle special frames produced internally (rather than
// captured/forwarded) by the remote router.
//
// We really shouldn't be decoding these above, since they are
// not genuine Ethernet frames. However, it is actually more
// efficient to do so, as we want to optimise for the common
// (i.e. non-special) frames. These always need decoding, and
// detecting special frames is cheaper post decoding than pre.
if decodedLen == 1 && dec.IsSpecial() {
if srcPeer == fwd.remotePeer && dstPeer == fwd.sleeve.localPeer {
select {
case fwd.specialChan <- specialFrame{sender, frame}:
case <-fwd.finishedChan:
}
}
return
}
sleeve.sendToConsumer(srcPeer, dstPeer, frame, dec)
}
func (sleeve *SleeveOverlay) sendToConsumer(srcPeer, dstPeer *mesh.Peer, frame []byte, dec *EthernetDecoder) {
if sleeve.consumer == nil {
return
}
fop := sleeve.consumer(ForwardPacketKey{
SrcPeer: srcPeer,
DstPeer: dstPeer,
PacketKey: dec.PacketKey(),
})
if fop != nil {
fop.Process(frame, dec, false)
}
}
type udpSender interface {
send([]byte, *net.UDPAddr) error
}
func (sleeve *SleeveOverlay) send(msg []byte, raddr *net.UDPAddr) error {
sleeve.lock.Lock()
conn := sleeve.conn
sleeve.lock.Unlock()
if conn == nil {
// Consume wasn't called yet
return nil
}
_, err := conn.WriteToUDP(msg, raddr)
return err
}
type sleeveCrypto struct {
Dec Decryptor
Enc Encryptor
EncDF Encryptor
}
func newSleeveCrypto(name []byte, sessionKey *[32]byte, outbound bool) sleeveCrypto {
if sessionKey == nil {
return sleeveCrypto{
Dec: NewNonDecryptor(),
Enc: NewNonEncryptor(name),
EncDF: NewNonEncryptor(name),
}
}
return sleeveCrypto{
Dec: NewNaClDecryptor(sessionKey, outbound),
Enc: NewNaClEncryptor(name, sessionKey, outbound, false),
EncDF: NewNaClEncryptor(name, sessionKey, outbound, true),
}
}
func (crypto sleeveCrypto) Overhead() int {
return UDPOverhead + crypto.EncDF.PacketOverhead() + crypto.EncDF.FrameOverhead() + EthernetOverhead
}
type sleeveForwarder struct {
// Immutable
sleeve *SleeveOverlay
remotePeer *mesh.Peer
remotePeerBin []byte
sendControlMsg func(byte, []byte) error
connUID uint64
// Channels to communicate with the aggregator goroutine
aggregatorChan chan<- aggregatorFrame
aggregatorDFChan chan<- aggregatorFrame
specialChan chan<- specialFrame
controlMsgChan chan<- controlMessage
confirmedChan chan<- struct{}
finishedChan <-chan struct{}
// listener channels
establishedChan chan struct{}
errorChan chan error
healthChan chan bool
// Explicitly locked state
lock sync.RWMutex
remoteAddr *net.UDPAddr
// These fields are accessed and updated independently, so no
// locking needed.
mtu int // the mtu for this link on the overlay network
stackFrag bool
// State only used within the forwarder goroutine
crypto sleeveCrypto
senderDF *udpSenderDF
maxPayload int
// How many bytes of overhead it takes to turn an IP packet on
// the overlay network into an encapsulated packet on the underlay
// network
overheadDF int
heartbeatInterval time.Duration
heartbeatTimer *time.Timer
heartbeatTimeout *time.Timer
fragTestTicker *time.Ticker
ackedHeartbeat bool
mtuTestTimeout *time.Timer
mtuTestsSent uint
mtuHighestGood int
mtuLowestBad int
mtuCandidate int
}
type aggregatorFrame struct {
src []byte
dst []byte
frame []byte
}
// A "special" frame over UDP
type specialFrame struct {
sender *net.UDPAddr
frame []byte
}
// A control message
type controlMessage struct {
tag byte
msg []byte
}
func (sleeve *SleeveOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
aggChan := make(chan aggregatorFrame, ChannelSize)
aggDFChan := make(chan aggregatorFrame, ChannelSize)
specialChan := make(chan specialFrame, 1)
controlMsgChan := make(chan controlMessage, 1)
confirmedChan := make(chan struct{})
finishedChan := make(chan struct{})
var remoteAddr *net.UDPAddr
if params.Outbound {
remoteAddr = makeUDPAddr(params.RemoteAddr)
}
crypto := newSleeveCrypto(sleeve.localPeer.NameByte, params.SessionKey, params.Outbound)
fwd := &sleeveForwarder{
sleeve: sleeve,
remotePeer: params.RemotePeer,
remotePeerBin: params.RemotePeer.NameByte,
sendControlMsg: params.SendControlMessage,
connUID: params.ConnUID,
aggregatorChan: aggChan,
aggregatorDFChan: aggDFChan,
specialChan: specialChan,
controlMsgChan: controlMsgChan,
confirmedChan: confirmedChan,
finishedChan: finishedChan,
establishedChan: make(chan struct{}),
errorChan: make(chan error, 1),
remoteAddr: remoteAddr,
mtu: DefaultMTU,
crypto: crypto,
maxPayload: DefaultMTU - UDPOverhead,
overheadDF: crypto.Overhead(),
senderDF: newUDPSenderDF(params.LocalAddr.IP, sleeve.localPort),
}
go fwd.run(aggChan, aggDFChan, specialChan, controlMsgChan, confirmedChan, finishedChan)
return fwd, nil
}
func (fwd *sleeveForwarder) logPrefixFor(sender *net.UDPAddr) string {
return fmt.Sprintf("sleeve ->[%s|%s]: ", sender, fwd.remotePeer)
}
func (fwd *sleeveForwarder) logPrefix() string {
fwd.lock.RLock()
remoteAddr := fwd.remoteAddr
fwd.lock.RUnlock()
return fwd.logPrefixFor(remoteAddr)
}
func (fwd *sleeveForwarder) Confirm() {
log.Debug(fwd.logPrefix(), "Confirm")
select {
case fwd.confirmedChan <- struct{}{}:
case <-fwd.finishedChan:
}
}
func (fwd *sleeveForwarder) EstablishedChannel() <-chan struct{} {
return fwd.establishedChan
}
func (fwd *sleeveForwarder) ErrorChannel() <-chan error {
return fwd.errorChan
}
func (fwd *sleeveForwarder) HealthChannel() <-chan bool {
return fwd.healthChan
}
type curriedForward struct {
NonDiscardingFlowOp
fwd *sleeveForwarder
key ForwardPacketKey
}
func (fwd *sleeveForwarder) Forward(key ForwardPacketKey) FlowOp {
return curriedForward{fwd: fwd, key: key}
}
func (f curriedForward) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
fwd := f.fwd
fwd.lock.RLock()
haveContact := (fwd.remoteAddr != nil)
mtu := fwd.mtu
stackFrag := fwd.stackFrag
fwd.lock.RUnlock()
if !haveContact {
log.Print(fwd.logPrefix(), "Cannot forward frame yet - awaiting contact")
return
}
srcName := f.key.SrcPeer.NameByte
dstName := f.key.DstPeer.NameByte
// We could use non-blocking channel sends here, i.e. drop frames
// on the floor when the forwarder is busy. This would allow our
// caller - the capturing loop in the router - to read frames more
// quickly when under load, i.e. we'd drop fewer frames on the
// floor during capture. And we could maximise CPU utilisation
// since we aren't stalling a thread. However, a lot of work has
// already been done by the time we get here. Since any packet we
// drop will likely get re-transmitted we end up paying that cost
// multiple times. So it's better to drop things at the beginning
// of our pipeline.
if dec.DF() {
if !frameTooBig(frame, mtu) {
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, frame)
return
}
// Why do we need an explicit broadcast hint here,
// rather than just checking the frame for a broadcast
// destination MAC address? Because even
// non-broadcast frames can be broadcast, if the
// destination MAC was not in our MAC cache.
if broadcast {
log.Print(fwd.logPrefix(), "dropping too big DF broadcast frame: len ", len(frame), " (", dec.IP.SrcIP, " -> ", dec.IP.DstIP, "): MTU=", mtu)
return
}
// Send an ICMP back to where the frame came from
fragNeededPacket, err := dec.makeICMPFragNeeded(mtu)
if err != nil {
log.Print(fwd.logPrefix(), err)
return
}
dec.DecodeLayers(fragNeededPacket)
// The frag-needed packet does not have DF set, so the
// potential recursion here is bounded.
fwd.sleeve.sendToConsumer(f.key.DstPeer, f.key.SrcPeer, fragNeededPacket, dec)
return
}
if stackFrag || len(dec.decoded) < 2 {
fwd.aggregate(fwd.aggregatorChan, srcName, dstName, frame)
return
}
// Don't have trustworthy stack, so we're going to have to
// send it DF in any case.
if !frameTooBig(frame, mtu) {
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, frame)
return
}
// We can't trust the stack to fragment, we have IP, and we
// have a frame that's too big for the MTU, so we have to
// fragment it ourself.
checkWarn(fragment(dec.Eth, dec.IP, mtu,
func(segFrame []byte) {
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, segFrame)
}))
}
func (fwd *sleeveForwarder) aggregate(ch chan<- aggregatorFrame, src []byte, dst []byte, frame []byte) {
select {
case ch <- aggregatorFrame{src, dst, frame}:
case <-fwd.finishedChan:
}
}
func fragment(eth layers.Ethernet, ip layers.IPv4, mtu int, forward func([]byte)) error {
// We are not doing any sort of NAT, so we don't need to worry
// about checksums of IP payload (eg UDP checksum).
headerSize := int(ip.IHL) * 4
// &^ is bit clear (AND NOT). So here we're clearing the lowest 3
// bits.
maxSegmentSize := (mtu - headerSize) &^ 7
opts := gopacket.SerializeOptions{
FixLengths: false,
ComputeChecksums: true}
payloadSize := int(ip.Length) - headerSize
payload := ip.BaseLayer.Payload[:payloadSize]
offsetBase := int(ip.FragOffset) << 3
origFlags := ip.Flags
ip.Flags = ip.Flags | layers.IPv4MoreFragments
ip.Length = uint16(headerSize + maxSegmentSize)
if eth.EthernetType == layers.EthernetTypeLLC {
// using LLC, so must set eth length correctly. eth length
// is just the length of the payload
eth.Length = ip.Length
} else {
eth.Length = 0
}
for offset := 0; offset < payloadSize; offset += maxSegmentSize {
var segmentPayload []byte
if len(payload) <= maxSegmentSize {
// last one
segmentPayload = payload
ip.Length = uint16(len(payload) + headerSize)
ip.Flags = origFlags
if eth.EthernetType == layers.EthernetTypeLLC {
eth.Length = ip.Length
} else {
eth.Length = 0
}
} else {
segmentPayload = payload[:maxSegmentSize]
payload = payload[maxSegmentSize:]
}
ip.FragOffset = uint16((offset + offsetBase) >> 3)
buf := gopacket.NewSerializeBuffer()
segPayload := gopacket.Payload(segmentPayload)
err := gopacket.SerializeLayers(buf, opts, &eth, &ip, &segPayload)
if err != nil {
return err
}
forward(buf.Bytes())
}
return nil
}
func frameTooBig(frame []byte, mtu int) bool {
// We capture/forward complete ethernet frames. Therefore the
// frame length includes the ethernet header. However, MTUs
// operate at the IP layer and thus do not include the ethernet
// header. To put it another way, when a sender that was told an
// MTU of M sends an IP packet of exactly that length, we will
// capture/forward M + EthernetOverhead bytes of data.
return len(frame) > mtu+EthernetOverhead
}
func (fwd *sleeveForwarder) ControlMessage(tag byte, msg []byte) {
select {
case fwd.controlMsgChan <- controlMessage{tag, msg}:
case <-fwd.finishedChan:
}
}
func (fwd *sleeveForwarder) Attrs() map[string]interface{} {
return map[string]interface{}{"name": "sleeve", "mtu": fwd.mtu}
}
func (fwd *sleeveForwarder) Stop() {
fwd.sleeve.removeForwarder(fwd.remotePeer.Name, fwd)
// Tell the forwarder goroutine to finish. We don't need to
// wait for it.
close(fwd.confirmedChan)
}
func (fwd *sleeveForwarder) run(aggChan <-chan aggregatorFrame,
aggDFChan <-chan aggregatorFrame,
specialChan <-chan specialFrame,
controlMsgChan <-chan controlMessage,
confirmedChan <-chan struct{},
finishedChan chan<- struct{}) {
defer close(finishedChan)
var err error
loop:
for err == nil {
select {
case frame := <-aggChan:
err = fwd.aggregateAndSend(frame, aggChan, fwd.crypto.Enc, fwd.sleeve, MaxUDPPacketSize-UDPOverhead)
case frame := <-aggDFChan:
err = fwd.aggregateAndSend(frame, aggDFChan, fwd.crypto.EncDF, fwd.senderDF, fwd.maxPayload)
case sf := <-specialChan:
err = fwd.handleSpecialFrame(sf)
case cm := <-controlMsgChan:
err = fwd.handleControlMessage(cm)
case _, ok := <-confirmedChan:
if !ok {
// confirmedChan is closed to indicate
// the forwarder is being closed
break loop
}
err = fwd.confirmed()
case <-timerChan(fwd.heartbeatTimer):
err = fwd.sendHeartbeat()
case <-timerChan(fwd.heartbeatTimeout):
err = fmt.Errorf("timed out waiting for UDP heartbeat")
case <-tickerChan(fwd.fragTestTicker):
err = fwd.sendFragTest()
case <-timerChan(fwd.mtuTestTimeout):
err = fwd.handleMTUTestFailure()
}
}
if fwd.heartbeatTimer != nil {
fwd.heartbeatTimer.Stop()
}
if fwd.heartbeatTimeout != nil {
fwd.heartbeatTimeout.Stop()
}
if fwd.fragTestTicker != nil {
fwd.fragTestTicker.Stop()
}
if fwd.mtuTestTimeout != nil {
fwd.mtuTestTimeout.Stop()
}
checkWarn(fwd.senderDF.close())
fwd.lock.RLock()
defer fwd.lock.RUnlock()
// this is the only place we send an error to errorChan
fwd.errorChan <- err
}
func (fwd *sleeveForwarder) aggregateAndSend(frame aggregatorFrame, aggChan <-chan aggregatorFrame, enc Encryptor, sender udpSender, limit int) error {
// Give up after processing N frames, to avoid starving the
// other activities of the forwarder goroutine.
i := 0
for {
// Adding the first frame to an empty buffer
if !fits(frame, enc, limit) {
log.Print(fwd.logPrefix(), "Dropping too big frame during forwarding: frame len ", len(frame.frame), ", limit ", limit)
return nil
}
for {
enc.AppendFrame(frame.src, frame.dst, frame.frame)
i++
gotOne := false
if i < 100 {
select {
case frame = <-aggChan:
gotOne = true
default:
}
}
if !gotOne {
return fwd.flushEncryptor(enc, sender)
}
// Accumulate frames until doing so would
// exceed the MTU. Even in the non-DF case,
// it doesn't seem worth adding a frame where
// that would lead to fragmentation,
// potentially delaying or risking other
// frames.
if !fits(frame, enc, fwd.maxPayload) {
break
}
}
if err := fwd.flushEncryptor(enc, sender); err != nil {
return err
}
}
}
func fits(frame aggregatorFrame, enc Encryptor, limit int) bool {
return enc.TotalLen()+enc.FrameOverhead()+len(frame.frame) <= limit
}
func (fwd *sleeveForwarder) flushEncryptor(enc Encryptor, sender udpSender) error {
msg, err := enc.Bytes()
if err != nil {
return err
}
return fwd.processSendError(sender.send(msg, fwd.remoteAddr))
}
func (fwd *sleeveForwarder) sendSpecial(enc Encryptor, sender udpSender, data []byte) error {
enc.AppendFrame(fwd.sleeve.localPeerBin, fwd.remotePeerBin, data)
return fwd.flushEncryptor(enc, sender)
}
func (fwd *sleeveForwarder) handleSpecialFrame(special specialFrame) error {
// The special frame types are distinguished by length
switch len(special.frame) {
case EthernetOverhead + 8:
return fwd.handleHeartbeat(special)
case FragTestSize:
return fwd.handleFragTest(special.frame)
default:
return fwd.handleMTUTest(special.frame)
}
}
func (fwd *sleeveForwarder) handleControlMessage(cm controlMessage) error {
switch cm.tag {
case ProtocolConnectionEstablished:
return fwd.handleHeartbeatAck()
case ProtocolFragmentationReceived:
return fwd.handleFragTestAck()
case ProtocolPMTUVerified:
return fwd.handleMTUTestAck(cm.msg)
default:
log.Print(fwd.logPrefix(), "Ignoring unknown control message tag: ", cm.tag)
return nil
}
}
func (fwd *sleeveForwarder) confirmed() error {
log.Debug(fwd.logPrefix(), "confirmed")
if fwd.heartbeatInterval != 0 {
// already confirmed
return nil
}
// when the connection is confirmed, this should be the only
// forwarder to the peer.
fwd.sleeve.addForwarder(fwd.remotePeer.Name, fwd)
// heartbeatInterval flags that we want to send heartbeats,
// even if we don't do sendHeartbeat() yet due to lacking the
// remote address.
fwd.heartbeatInterval = FastHeartbeat
if fwd.remoteAddr != nil {
if err := fwd.sendHeartbeat(); err != nil {
return err
}
}
fwd.heartbeatTimeout = time.NewTimer(HeartbeatTimeout)
return nil
}
func (fwd *sleeveForwarder) sendHeartbeat() error {
log.Debug(fwd.logPrefix(), "sendHeartbeat")
// Prime the timer for the next heartbeat. We don't use a
// ticker because the interval is not constant.
fwd.heartbeatTimer = setTimer(fwd.heartbeatTimer, fwd.heartbeatInterval)
buf := make([]byte, EthernetOverhead+8)
binary.BigEndian.PutUint64(buf[EthernetOverhead:], fwd.connUID)
return fwd.sendSpecial(fwd.crypto.EncDF, fwd.senderDF, buf)
}
func (fwd *sleeveForwarder) handleHeartbeat(special specialFrame) error {
uid := binary.BigEndian.Uint64(special.frame[EthernetOverhead:])
if uid != fwd.connUID {
return nil
}
log.Debug(fwd.logPrefix(), "handleHeartbeat")
if fwd.remoteAddr == nil {
fwd.setRemoteAddr(special.sender)
if fwd.heartbeatInterval != 0 {
if err := fwd.sendHeartbeat(); err != nil {
return err
}
}
} else if !udpAddrsEqual(fwd.remoteAddr, special.sender) {
log.Print(fwd.logPrefix(), "Peer UDP address changed to ", special.sender)
fwd.setRemoteAddr(special.sender)
}
if !fwd.ackedHeartbeat {
fwd.ackedHeartbeat = true
if err := fwd.sendControlMsg(ProtocolConnectionEstablished, nil); err != nil {
return err
}
}
// we can receive a heartbeat before confirmed() has set up
// heartbeatTimeout
if fwd.heartbeatTimeout != nil {
fwd.heartbeatTimeout.Reset(HeartbeatTimeout)
}
return nil
}
func (fwd *sleeveForwarder) setRemoteAddr(addr *net.UDPAddr) {
// remoteAddr is only modified here, so we don't need to hold
// the lock when reading it from the forwarder goroutine. But
// other threads may read it while holding the read lock, so
// when we modify it, we need to hold the write lock.
fwd.lock.Lock()
fwd.remoteAddr = addr
fwd.lock.Unlock()
}
func (fwd *sleeveForwarder) handleHeartbeatAck() error {
log.Debug(fwd.logPrefix(), "handleHeartbeatAck")
if fwd.heartbeatInterval != SlowHeartbeat {
fwd.heartbeatInterval = SlowHeartbeat
if fwd.heartbeatTimer != nil {
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
}
// The connection is now regarded as established
close(fwd.establishedChan)
}
fwd.fragTestTicker = time.NewTicker(FragTestInterval)
if err := fwd.sendFragTest(); err != nil {
return err
}
// Send a large frame down the DF channel. An EMSGSIZE will
// result, which is handled in processSendError, prompting
// PMTU discovery to start.
return fwd.sendSpecial(fwd.crypto.EncDF, fwd.senderDF, make([]byte, PMTUDiscoverySize))
}
func (fwd *sleeveForwarder) sendFragTest() error {
log.Debug(fwd.logPrefix(), "sendFragTest")
fwd.stackFrag = false
return fwd.sendSpecial(fwd.crypto.Enc, fwd.sleeve, make([]byte, FragTestSize))
}
func (fwd *sleeveForwarder) handleFragTest(frame []byte) error {
if !allZeros(frame) {
return nil
}
return fwd.sendControlMsg(ProtocolFragmentationReceived, nil)
}
func (fwd *sleeveForwarder) handleFragTestAck() error {
log.Debug(fwd.logPrefix(), "handleFragTestAck")
fwd.stackFrag = true
return nil
}
func (fwd *sleeveForwarder) processSendError(err error) error {
if mtbe, ok := err.(msgTooBigError); ok {
mtu := mtbe.underlayPMTU - fwd.overheadDF
if fwd.mtuCandidate != 0 && mtu >= fwd.mtuCandidate {
return nil
}
fwd.mtuHighestGood = 552
fwd.mtuLowestBad = mtu + 1
fwd.mtuCandidate = mtu
fwd.mtuTestsSent = 0
fwd.maxPayload = mtbe.underlayPMTU - UDPOverhead
fwd.mtu = mtu
return fwd.sendMTUTest()
}
return err
}
func (fwd *sleeveForwarder) sendMTUTest() error {
log.Debug(fwd.logPrefix(), "sendMTUTest: mtu candidate ", fwd.mtuCandidate)
err := fwd.sendSpecial(fwd.crypto.EncDF, fwd.senderDF, make([]byte, fwd.mtuCandidate+EthernetOverhead))
if err != nil {
return err
}
fwd.mtuTestTimeout = setTimer(fwd.mtuTestTimeout, MTUVerifyTimeout<<fwd.mtuTestsSent)
fwd.mtuTestsSent++
return nil
}
func (fwd *sleeveForwarder) handleMTUTest(frame []byte) error {
buf := make([]byte, 2)
binary.BigEndian.PutUint16(buf, uint16(len(frame)-EthernetOverhead))
return fwd.sendControlMsg(ProtocolPMTUVerified, buf)
}
func (fwd *sleeveForwarder) handleMTUTestAck(msg []byte) error {
if len(msg) < 2 {
log.Print(fwd.logPrefix(), "Received truncated MTUTestAck")
return nil
}
mtu := int(binary.BigEndian.Uint16(msg))
log.Debug(fwd.logPrefix(), "handleMTUTestAck: for mtu candidate ", mtu)
if mtu != fwd.mtuCandidate {
return nil
}
fwd.mtuHighestGood = mtu
return fwd.searchMTU()
}
func (fwd *sleeveForwarder) handleMTUTestFailure() error {
if fwd.mtuTestsSent < MTUVerifyAttempts {
return fwd.sendMTUTest()
}
log.Debug(fwd.logPrefix(), "handleMTUTestFailure")
fwd.mtuLowestBad = fwd.mtuCandidate
return fwd.searchMTU()
}
func (fwd *sleeveForwarder) searchMTU() error {
log.Debug(fwd.logPrefix(), "searchMTU: ", fwd.mtuHighestGood, fwd.mtuLowestBad)
if fwd.mtuHighestGood+1 >= fwd.mtuLowestBad {
mtu := fwd.mtuHighestGood
log.Print(fwd.logPrefix(), "Effective MTU verified at ", mtu)
if fwd.mtuTestTimeout != nil {
fwd.mtuTestTimeout.Stop()
fwd.mtuTestTimeout = nil
}
fwd.mtuCandidate = 0
fwd.maxPayload = mtu + fwd.overheadDF - UDPOverhead
fwd.mtu = mtu
return nil
}
fwd.mtuCandidate = (fwd.mtuHighestGood + fwd.mtuLowestBad) / 2
fwd.mtuTestsSent = 0
return fwd.sendMTUTest()
}
type udpSenderDF struct {
ipBuf gopacket.SerializeBuffer
opts gopacket.SerializeOptions
udpHeader *layers.UDP
localIP net.IP
remoteIP net.IP
socket *net.IPConn
}
func newUDPSenderDF(localIP net.IP, localPort int) *udpSenderDF {
return &udpSenderDF{
ipBuf: gopacket.NewSerializeBuffer(),
opts: gopacket.SerializeOptions{
FixLengths: true,
// UDP header is calculated with a phantom IP
// header. Yes, it's totally nuts. Thankfully,
// for UDP over IPv4, the checksum is
// optional. It's not optional for IPv6, but
// we'll ignore that for now. TODO
ComputeChecksums: false,
},
udpHeader: &layers.UDP{SrcPort: layers.UDPPort(localPort)},
localIP: localIP,
}
}
func (sender *udpSenderDF) dial() error {
if sender.socket != nil {
if err := sender.socket.Close(); err != nil {
return err
}
sender.socket = nil
}
laddr := &net.IPAddr{IP: sender.localIP}
raddr := &net.IPAddr{IP: sender.remoteIP}
s, err := net.DialIP("ip4:UDP", laddr, raddr)
if err != nil {
return err
}
f, err := s.File()
if err != nil {
return err
}
defer f.Close()
// This makes sure all packets we send out have DF set on them.
err = syscall.SetsockoptInt(int(f.Fd()), syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DO)
if err != nil {
return err
}
sender.socket = s
return nil
}
func (sender *udpSenderDF) send(msg []byte, raddr *net.UDPAddr) error {
// Ensure we have a socket sending to the right IP address
if sender.socket == nil || !sender.remoteIP.Equal(raddr.IP) {
sender.remoteIP = raddr.IP
if err := sender.dial(); err != nil {
return err
}
}
sender.udpHeader.DstPort = layers.UDPPort(raddr.Port)
payload := gopacket.Payload(msg)
err := gopacket.SerializeLayers(sender.ipBuf, sender.opts, sender.udpHeader, &payload)
if err != nil {
return err
}
packet := sender.ipBuf.Bytes()
_, err = sender.socket.Write(packet)
if err == nil || PosixError(err) != syscall.EMSGSIZE {
return err
}
f, err := sender.socket.File()
if err != nil {
return err
}
defer f.Close()
log.Print("EMSGSIZE on send, expecting PMTU update (IP packet was ", len(packet), " bytes, payload was ", len(msg), " bytes)")
pmtu, err := syscall.GetsockoptInt(int(f.Fd()), syscall.IPPROTO_IP, syscall.IP_MTU)
if err != nil {
return err
}
return msgTooBigError{underlayPMTU: pmtu}
}
type msgTooBigError struct {
underlayPMTU int // actual pmtu, i.e. what the kernel told us
}
func (mtbe msgTooBigError) Error() string {
return fmt.Sprint("Msg too big error. PMTU is ", mtbe.underlayPMTU)
}
func (sender *udpSenderDF) close() error {
if sender.socket == nil {
return nil
}
return sender.socket.Close()
}
func udpAddrsEqual(a *net.UDPAddr, b *net.UDPAddr) bool {
return a.IP.Equal(b.IP) && a.Port == b.Port && a.Zone == b.Zone
}
func allZeros(s []byte) bool {
for _, b := range s {
if b != byte(0) {
return false
}
}
return true
}
func setTimer(timer *time.Timer, d time.Duration) *time.Timer {
if timer == nil {
return time.NewTimer(d)
}
timer.Reset(d)
return timer
}
func timerChan(timer *time.Timer) <-chan time.Time {
if timer != nil {
return timer.C
}
return nil
}
func tickerChan(ticker *time.Ticker) <-chan time.Time {
if ticker != nil {
return ticker.C
}
return nil
}
func makeUDPAddr(addr *net.TCPAddr) *net.UDPAddr {
return &net.UDPAddr{IP: addr.IP, Port: addr.Port, Zone: addr.Zone}
}
// Look inside an error produced by the net package to get to the
// syscall.Errno at the root of the problem.
func PosixError(err error) error {
if operr, ok := err.(*net.OpError); ok {
err = operr.Err
}
// go1.5 wraps an Errno inside a SyscallError inside an OpError
if scerr, ok := err.(*os.SyscallError); ok {
err = scerr.Err
}
return err
}