Files
weave/router/sleeve.go
T
David Wragg bd185abb29 Introduce PeerShortIDs
These are needed to support vxlan encapsulation.  In a vxlan packet,
the only place we can straightforwardly hold the source and
destination peers identifiers used by the weave router is in the
24-bit VNI (vxlan network identifier) field.  This gives us 12 bits to
indicate a peer. The resulting limit of 4096 peers on a weave network
does not seem like a problem in the near future.  But it does mean
that decentralized allocation makes collisions fairly likely even in
weave networks of sizes that are common today.  Fortunately, the
problem is much more tractable than IPAM because we can detect when
collisions occur and take action to resolve them.

TODO: We already notify the InterHost when a short id -> peer mapping
changes, but we also need to flush any entries in the router MAC cache
that might be affected.
2015-06-12 16:22:13 +01:00

1099 lines
27 KiB
Go

// This contains the InterHost 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 (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
"net"
"sync"
"syscall"
"time"
"code.google.com/p/gopacket"
"code.google.com/p/gopacket/layers"
)
type SleeveInterHost struct {
localPort int
// These fields are set in ConsumePackets, and not
// subsequently modified
localPeer *Peer
localPeerBin []byte
consumer InterHostConsumer
peers *Peers
conn *net.UDPConn
lock sync.Mutex
forwarders map[PeerName]*sleeveForwarder
}
func NewSleeveInterHost(localPort int) InterHost {
return &SleeveInterHost{localPort: localPort}
}
func (sleeve *SleeveInterHost) ConsumePackets(localPeer *Peer, peers *Peers,
consumer InterHostConsumer) error {
localAddr, err := net.ResolveUDPAddr("udp4",
fmt.Sprint(":", 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("ConsumePackets already called")
}
sleeve.localPeer = localPeer
sleeve.localPeerBin = localPeer.NameByte
sleeve.consumer = consumer
sleeve.peers = peers
sleeve.conn = conn
sleeve.forwarders = make(map[PeerName]*sleeveForwarder)
go sleeve.readUDP()
return nil
}
func (*SleeveInterHost) InvalidateRoutes() {
// no cached information, so nothing to do
}
func (*SleeveInterHost) InvalidateShortIDs() {
// no cached information, so nothing to do
}
func (sleeve *SleeveInterHost) logFrame(string, []byte, *layers.Ethernet) {
// XXX
}
func (sleeve *SleeveInterHost) lookupForwarder(peer PeerName) *sleeveForwarder {
sleeve.lock.Lock()
defer sleeve.lock.Unlock()
return sleeve.forwarders[peer]
}
func (sleeve *SleeveInterHost) 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.Println("ignoring UDP read error", err)
continue
} else if n < NameSize {
log.Println("ignoring too short UDP packet from", sender)
continue
}
fwdName := 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.
fwd.logFrom(sender, err)
}
}
}
func (sleeve *SleeveInterHost) 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, found := sleeve.peers.Fetch(PeerNameFromBin(src))
if !found {
return
}
dstPeer, found := sleeve.peers.Fetch(PeerNameFromBin(dst))
if !found {
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 *SleeveInterHost) sendToConsumer(srcPeer, dstPeer *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.Send(frame, dec, false)
}
}
type udpSender interface {
send([]byte, *net.UDPAddr) error
}
func (sleeve *SleeveInterHost) 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 sleeveForwarder struct {
// Immutable
sleeve *SleeveInterHost
remotePeer *Peer
remotePeerBin []byte
sendControlMsg func([]byte) error
uid uint64
// Channels to communicate with the aggregator goroutine
aggregatorChan chan<- aggregatorFrame
aggregatorDFChan chan<- aggregatorFrame
specialChan chan<- specialFrame
confirmedChan chan<- struct{}
finishedChan <-chan struct{}
// Explicitly locked state
lock sync.RWMutex
listener InterHostForwarderListener
remoteAddr *net.UDPAddr
// These fields are accessed and updated independently, so no
// locking needed
effectivePMTU int
stackFrag bool
// dec is only used from the readUDP goroutine, enc and encDF
// are only used in the forwarder goroutine
crypto InterHostCrypto
// State only used within the forwarder goroutine
senderDF *udpSenderDF
maxPayload int
heartbeatInterval time.Duration
heartbeatTimer *time.Timer
heartbeatTimeout *time.Timer
fragTestTicker *time.Ticker
ackedHeartbeat bool
pmtuTestTimeout *time.Timer
pmtuTestsSent uint
epmtuHighestGood int
epmtuLowestBad int
epmtuCandidate int
}
type aggregatorFrame struct {
src []byte
dst []byte
frame []byte
}
// A "special" message over UDP, or a control message. The sender is
// nil for control messages.
type specialFrame struct {
sender *net.UDPAddr
frame []byte
}
func (sleeve *SleeveInterHost) MakeForwarder(remotePeer *Peer,
localIP net.IP, remote *net.UDPAddr,
connUid uint64, crypto InterHostCrypto,
sendControlMsg func([]byte) error) (InterHostForwarder, error) {
aggChan := make(chan aggregatorFrame, ChannelSize)
aggDFChan := make(chan aggregatorFrame, ChannelSize)
specialChan := make(chan specialFrame, 1)
confirmedChan := make(chan struct{})
finishedChan := make(chan struct{})
remotePeerBin := remotePeer.NameByte
fwd := &sleeveForwarder{
sleeve: sleeve,
remotePeer: remotePeer,
remotePeerBin: remotePeerBin,
sendControlMsg: sendControlMsg,
uid: connUid,
aggregatorChan: aggChan,
aggregatorDFChan: aggDFChan,
specialChan: specialChan,
confirmedChan: confirmedChan,
finishedChan: finishedChan,
remoteAddr: remote,
effectivePMTU: DefaultPMTU,
crypto: crypto,
senderDF: newUDPSenderDF(localIP, sleeve.localPort),
maxPayload: DefaultPMTU - UDPOverhead,
}
// XXX handle case where remotePeer already registered
sleeve.lock.Lock()
sleeve.forwarders[remotePeer.Name] = fwd
sleeve.lock.Unlock()
go fwd.run(aggChan, aggDFChan, specialChan, confirmedChan,
finishedChan)
return fwd, nil
}
func (fwd *sleeveForwarder) logFrom(sender *net.UDPAddr, args ...interface{}) {
parts := make([]interface{}, len(args)+1)
parts[0] = fmt.Sprintf("->[%s|%s]:", sender, fwd.remotePeer)
copy(parts[1:], args)
log.Println(parts...)
}
func (fwd *sleeveForwarder) log(args ...interface{}) {
fwd.lock.RLock()
remoteAddr := fwd.remoteAddr
fwd.lock.RUnlock()
fwd.logFrom(remoteAddr, args...)
}
func (fwd *sleeveForwarder) debug(args ...interface{}) {
fwd.lock.RLock()
remoteAddr := fwd.remoteAddr
fwd.lock.RUnlock()
fwd.logFrom(remoteAddr, args...)
}
func (fwd *sleeveForwarder) SetListener(listener InterHostForwarderListener) {
fwd.debug("SetListener", listener)
fwd.lock.Lock()
fwd.listener = listener
fwd.lock.Unlock()
// Setting the listener confirms that the forwarder is really
// wanted
if listener != nil {
select {
case fwd.confirmedChan <- struct{}{}:
case <-fwd.finishedChan:
}
}
}
type curriedForward struct {
fwd *sleeveForwarder
key ForwardPacketKey
}
func (fwd *sleeveForwarder) Forward(key ForwardPacketKey) FlowOp {
return curriedForward{fwd, key}
}
func (f curriedForward) Send(frame []byte, dec *EthernetDecoder,
broadcast bool) {
fwd := f.fwd
fwd.lock.RLock()
haveContact := (fwd.remoteAddr != nil)
effectivePMTU := fwd.effectivePMTU
stackFrag := fwd.stackFrag
fwd.lock.RUnlock()
if !haveContact {
fwd.log("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, effectivePMTU) {
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.Printf("dropping too big DF broadcast frame (%v -> %v): PMTU= %v\n", dec.IP.DstIP, dec.IP.SrcIP, effectivePMTU)
return
}
// Send an ICMP back to where the frame came from
fn, err := dec.makeICMPFragNeeded(effectivePMTU)
if err != nil {
log.Println(err)
return
}
dec.DecodeLayers(fn)
// 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, fn, 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, effectivePMTU) {
fwd.aggregate(fwd.aggregatorDFChan, srcName, dstName, frame)
return
}
fwd.sleeve.logFrame("Fragmenting", frame, &dec.Eth)
// 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, effectivePMTU,
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, pmtu 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 := (pmtu - 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, effectivePMTU 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) > effectivePMTU+EthernetOverhead
}
func (fwd *sleeveForwarder) ControlMessage(msg []byte) {
select {
case fwd.specialChan <- specialFrame{nil, msg}:
case <-fwd.finishedChan:
}
}
func (fwd *sleeveForwarder) Close() {
sleeve := fwd.sleeve
sleeve.lock.Lock()
if sleeve.forwarders[fwd.remotePeer.Name] == fwd {
delete(sleeve.forwarders, fwd.remotePeer.Name)
}
sleeve.lock.Unlock()
fwd.SetListener(nil)
// 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,
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 special := <-specialChan:
err = fwd.handleSpecialFrame(special)
case _, ok := <-confirmedChan:
if !ok {
// specialChan 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.pmtuTestTimeout):
err = fwd.handlePMTUTestFailure()
}
}
if fwd.heartbeatTimer != nil {
fwd.heartbeatTimer.Stop()
}
if fwd.heartbeatTimeout != nil {
fwd.heartbeatTimeout.Stop()
}
if fwd.fragTestTicker != nil {
fwd.fragTestTicker.Stop()
}
if fwd.pmtuTestTimeout != nil {
fwd.pmtuTestTimeout.Stop()
}
checkWarn(fwd.senderDF.close())
fwd.lock.RLock()
defer fwd.lock.RUnlock()
if fwd.listener != nil {
fwd.listener.Error(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) {
fwd.log("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 PMTU. 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 {
if special.sender == nil {
return fwd.handleControlMsg(special.frame)
}
// 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.handlePMTUTest(special.frame)
}
}
const (
HeartbeatAck = iota
FragTestAck
PMTUTestAck
)
func (fwd *sleeveForwarder) handleControlMsg(msg []byte) error {
if len(msg) == 0 {
fwd.log("Received zero-length control message")
return nil
}
switch msg[0] {
case HeartbeatAck:
return fwd.handleHeartbeatAck()
case FragTestAck:
return fwd.handleFragTestAck()
case PMTUTestAck:
return fwd.handlePMTUTestAck(msg)
default:
fwd.log("Ignoring unknown control message:", msg[0])
return nil
}
}
func (fwd *sleeveForwarder) confirmed() error {
fwd.debug("confirmed")
if fwd.heartbeatInterval != 0 {
// already confirmed
return nil
}
// heartbeatInterval flags that we want to send heartbeats,
// even if we don't do sendHeartbeat() yet due to lacking the
// remote address.
fwd.heartbeatInterval = time.Duration(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 {
fwd.debug("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.uid)
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.uid {
return nil
}
fwd.debug("handleHeartbeat")
if fwd.remoteAddr == nil {
fwd.setRemoteAddr(special.sender)
if fwd.heartbeatInterval != time.Duration(0) {
if err := fwd.sendHeartbeat(); err != nil {
return err
}
}
} else if !udpAddrsEqual(fwd.remoteAddr, special.sender) {
fwd.log("Peer UDP address changed to", special.sender)
fwd.setRemoteAddr(special.sender)
}
if !fwd.ackedHeartbeat {
fwd.ackedHeartbeat = true
if err := fwd.sendControlMsg([]byte{HeartbeatAck}); err != nil {
return err
}
}
fwd.heartbeatTimeout.Reset(HeartbeatTimeout)
return nil
}
func (fwd *sleeveForwarder) setRemoteAddr(addr *net.UDPAddr) {
// Although we don't need to lock when reading remoteAddr,
// because this thread is the only one that modifies
// remoteAddr, we do need to lock when writing it, because
// memory models.
fwd.lock.Lock()
fwd.remoteAddr = addr
fwd.lock.Unlock()
}
func (fwd *sleeveForwarder) handleHeartbeatAck() error {
// The connection is nowregarded as established
fwd.notifyEstablished()
if fwd.heartbeatInterval != SlowHeartbeat {
fwd.heartbeatInterval = SlowHeartbeat
if fwd.heartbeatTimer != nil {
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
}
}
fwd.fragTestTicker = time.NewTicker(FragTestInterval)
if err := fwd.sendFragTest(); err != nil {
return err
}
// Send a large frame down the DF channel in order to prompt
// PMTU discovery to start.
return fwd.sendSpecial(fwd.crypto.EncDF, fwd.senderDF,
make([]byte, PMTUDiscoverySize))
}
func (fwd *sleeveForwarder) notifyEstablished() {
fwd.lock.RLock()
defer fwd.lock.RUnlock()
if fwd.listener != nil {
fwd.listener.Established()
}
}
func (fwd *sleeveForwarder) sendFragTest() error {
fwd.debug("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([]byte{FragTestAck})
}
func (fwd *sleeveForwarder) handleFragTestAck() error {
fwd.debug("handleFragTestAck")
fwd.stackFrag = true
return nil
}
func (fwd *sleeveForwarder) processSendError(err error) error {
if mtbe, ok := err.(msgTooBigError); ok {
epmtu := mtbe.PMTU - fwd.overhead()
if fwd.epmtuCandidate != 0 && epmtu >= fwd.epmtuCandidate {
return nil
}
fwd.epmtuHighestGood = 8
fwd.epmtuLowestBad = epmtu + 1
fwd.epmtuCandidate = epmtu
fwd.pmtuTestsSent = 0
fwd.maxPayload = mtbe.PMTU - UDPOverhead
fwd.effectivePMTU = epmtu
return fwd.sendPMTUTest()
}
return err
}
// How many bytes of overhead it takes to turn an IP packet on the
// overlay network into an encapsulated packet on the underlay network
func (fwd *sleeveForwarder) overhead() int {
return UDPOverhead + fwd.crypto.EncDF.PacketOverhead() +
fwd.crypto.EncDF.FrameOverhead() + EthernetOverhead
}
func (fwd *sleeveForwarder) sendPMTUTest() error {
fwd.debug("sendPMTUTest: epmtu candidate", fwd.epmtuCandidate)
err := fwd.sendSpecial(fwd.crypto.EncDF, fwd.senderDF,
make([]byte, fwd.epmtuCandidate+EthernetOverhead))
if err != nil {
return err
}
fwd.pmtuTestTimeout = setTimer(fwd.pmtuTestTimeout,
PMTUVerifyTimeout<<fwd.pmtuTestsSent)
fwd.pmtuTestsSent++
return nil
}
func (fwd *sleeveForwarder) handlePMTUTest(frame []byte) error {
buf := make([]byte, 3)
buf[0] = PMTUTestAck
binary.BigEndian.PutUint16(buf[1:], uint16(len(frame)-EthernetOverhead))
return fwd.sendControlMsg(buf)
}
func (fwd *sleeveForwarder) handlePMTUTestAck(msg []byte) error {
if len(msg) < 3 {
fwd.log("Received truncated PMTUTestAck")
return nil
}
epmtu := int(binary.BigEndian.Uint16(msg[1:]))
fwd.debug("handlePMTUTestAck: for epmtu candidate", epmtu)
if epmtu != fwd.epmtuCandidate {
return nil
}
fwd.epmtuHighestGood = epmtu
return fwd.searchEPMTU()
}
func (fwd *sleeveForwarder) handlePMTUTestFailure() error {
if fwd.pmtuTestsSent < PMTUVerifyAttempts {
return fwd.sendPMTUTest()
}
fwd.debug("handlePMTUTestFailure")
fwd.epmtuLowestBad = fwd.epmtuCandidate
return fwd.searchEPMTU()
}
func (fwd *sleeveForwarder) searchEPMTU() error {
fwd.debug("searchEPMTU:", fwd.epmtuHighestGood, fwd.epmtuLowestBad)
if fwd.epmtuHighestGood+1 >= fwd.epmtuLowestBad {
epmtu := fwd.epmtuHighestGood
fwd.log("Effective PMTU verified at", epmtu)
if fwd.pmtuTestTimeout != nil {
fwd.pmtuTestTimeout.Stop()
fwd.pmtuTestTimeout = nil
}
fwd.epmtuCandidate = 0
fwd.maxPayload = epmtu + fwd.overhead() - UDPOverhead
fwd.effectivePMTU = epmtu
return nil
}
fwd.epmtuCandidate = (fwd.epmtuHighestGood + fwd.epmtuLowestBad) / 2
fwd.pmtuTestsSent = 0
return fwd.sendPMTUTest()
}
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)
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 || !bytes.Equal(sender.remoteIP, 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.Println("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{PMTU: pmtu}
}
type msgTooBigError struct {
PMTU 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.PMTU)
}
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 bytes.Equal(a.IP, 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
}