Files
weave/router/fastdp.go
T

1399 lines
36 KiB
Go

package router
import (
"encoding/binary"
"encoding/json"
"fmt"
"net"
"sync"
"syscall"
"time"
"github.com/pkg/errors"
"github.com/vishvananda/netlink"
"github.com/weaveworks/go-odp/odp"
"github.com/weaveworks/mesh"
"github.com/weaveworks/weave/net/ipsec"
)
// The virtual bridge accepts packets from ODP vports and the router
// port (i.e. InjectPacket). We need a map key to index those
// possibilities:
type bridgePortID struct {
vport odp.VportID
router bool
}
// A bridgeSender sends out a packet from the virtual bridge
type bridgeSender func(key PacketKey, lock *fastDatapathLock) FlowOp
// A missHandler handles an ODP miss
type missHandler func(fks odp.FlowKeys, lock *fastDatapathLock) FlowOp
type FastDatapath struct {
lock sync.Mutex // guards state and synchronises use of dpif
iface *net.Interface
dpif *odp.Dpif
dp odp.DatapathHandle
deleteFlowsCount uint64
missCount uint64
missHandlers map[odp.VportID]missHandler
localPeer *mesh.Peer
peers *mesh.Peers
overlayConsumer OverlayConsumer
ipsec *ipsec.IPSec
// Bridge state: How to send to the given bridge port
sendToPort map[bridgePortID]bridgeSender
// How to send to a given destination MAC
sendToMAC map[MAC]bridgeSender
// MACs seen on the bridge recently
seenMACs map[MAC]struct{}
// vxlan vports associated with the given UDP ports
vxlanUDPPorts map[int]odp.VportID
vxlanVportIDs map[odp.VportID]struct{}
mainVxlanVportID odp.VportID
mainVxlanUDPPort int
// A singleton pool for the occasions when we need to decode
// the packet.
dec *EthernetDecoder
// forwarders by remote peer
forwarders map[mesh.PeerName]*fastDatapathForwarder
}
func NewFastDatapath(iface *net.Interface, port int, encryptionEnabled bool) (*FastDatapath, error) {
var ipSec *ipsec.IPSec
dpif, err := odp.NewDpif()
if err != nil {
return nil, err
}
success := false
defer func() {
if !success {
dpif.Close()
}
}()
dp, err := dpif.LookupDatapath(iface.Name)
if err != nil {
return nil, err
}
if encryptionEnabled {
var err error
if ipSec, err = ipsec.New(log); err != nil {
return nil, errors.Wrap(err, "ipsec new")
}
if err := ipSec.Flush(false); err != nil {
return nil, errors.Wrap(err, "ipsec flush")
}
}
fastdp := &FastDatapath{
iface: iface,
dpif: dpif,
dp: dp,
missHandlers: make(map[odp.VportID]missHandler),
ipsec: ipSec,
sendToPort: nil,
sendToMAC: make(map[MAC]bridgeSender),
seenMACs: make(map[MAC]struct{}),
vxlanUDPPorts: make(map[int]odp.VportID),
vxlanVportIDs: make(map[odp.VportID]struct{}),
forwarders: make(map[mesh.PeerName]*fastDatapathForwarder),
}
// This delete happens asynchronously in the kernel, meaning that
// we can sometimes fail to recreate the vxlan vport with EADDRINUSE -
// consequently we retry a small number of times in
// getVxlanVportIDHarder() to compensate.
if err := fastdp.deleteVxlanVports(); err != nil {
return nil, err
}
if err := fastdp.deleteFlows(); err != nil {
return nil, err
}
// We use the weave port number plus 1 for vxlan. Hard-coding
// this relationship may seem dubious, but there is no moral
// difference between this and requiring that the sleeve UDP
// port number is the same as the TCP port number. The hard
// part would be not adding weaver flags to allow the port
// numbers to be independent, but working out how to specify
// them on the connecting side. So we can wait to find out if
// anyone wants that.
fastdp.mainVxlanUDPPort = port + 1
fastdp.mainVxlanVportID, err = fastdp.getVxlanVportIDHarder(fastdp.mainVxlanUDPPort, 5, time.Millisecond*10)
if err != nil {
return nil, err
}
// need to lock before we might receive events
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
if _, err := dp.ConsumeMisses(fastdp); err != nil {
return nil, err
}
if _, err := dp.ConsumeVportEvents(fastdp); err != nil {
return nil, err
}
vports, err := dp.EnumerateVports()
if err != nil {
return nil, err
}
for _, vport := range vports {
fastdp.makeBridgeVport(vport)
}
success = true
go fastdp.run()
return fastdp, nil
}
func (fastdp *FastDatapath) Close() error {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
err := fastdp.dpif.Close()
fastdp.dpif = nil
return err
}
// While processing a packet, we can potentially acquire and drop the
// FastDatapath lock many times (acquiring it to acceess FastDatapath
// state, and invoke ODP operations; dropping it to invoke callbacks
// that may re-enter the FastDatapath). A fastDatapathLock
// coordinates this process.
type fastDatapathLock struct {
fastdp *FastDatapath
locked bool
// While the lock is dropped, deleteFlows could be called. We
// need to detect when this happens and avoid creating flows,
// because they may be based on stale information.
deleteFlowsCount uint64
}
func (fastdp *FastDatapath) startLock() fastDatapathLock {
fastdp.lock.Lock()
return fastDatapathLock{
fastdp: fastdp,
locked: true,
deleteFlowsCount: fastdp.deleteFlowsCount,
}
}
func (lock *fastDatapathLock) unlock() {
if lock.locked {
lock.fastdp.lock.Unlock()
lock.locked = false
}
}
func (lock *fastDatapathLock) relock() {
if !lock.locked {
lock.fastdp.lock.Lock()
lock.locked = true
}
}
// InjectorConsumer bits
type fastDatapathInjectorConsumer struct {
*FastDatapath
}
func (fastdp *FastDatapath) InjectorConsumer() InjectorConsumer {
return fastDatapathInjectorConsumer{fastdp}
}
func (fastdp fastDatapathInjectorConsumer) Interface() *net.Interface {
return fastdp.iface
}
func (fastdp fastDatapathInjectorConsumer) String() string {
return fmt.Sprint(fastdp.iface.Name, " (via ODP)")
}
func (fastdp fastDatapathInjectorConsumer) Stats() map[string]int {
lock := fastdp.startLock()
defer lock.unlock()
return map[string]int{
"FlowMisses": int(fastdp.missCount),
}
}
var routerBridgePortID = bridgePortID{router: true}
func (fastdp fastDatapathInjectorConsumer) StartConsumingPackets(consumer Consumer) error {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
if fastdp.sendToPort[routerBridgePortID] != nil {
return fmt.Errorf("FastDatapath already has a Consumer")
}
// set up delivery to the weave router port on the bridge
fastdp.addSendToPort(routerBridgePortID,
func(key PacketKey, lock *fastDatapathLock) FlowOp {
// drop the FastDatapath lock in order to call
// the consumer
lock.unlock()
return consumer(key)
})
return nil
}
func (fastdp fastDatapathInjectorConsumer) InjectPacket(key PacketKey) FlowOp {
lock := fastdp.startLock()
defer lock.unlock()
return fastdp.bridge(routerBridgePortID, key, &lock)
}
// Ethernet bridge implementation
func (fastdp *FastDatapath) bridge(ingress bridgePortID, key PacketKey, lock *fastDatapathLock) FlowOp {
lock.relock()
if fastdp.sendToMAC[key.SrcMAC] == nil {
// Learn the source MAC
fastdp.sendToMAC[key.SrcMAC] = fastdp.sendToPort[ingress]
fastdp.seenMACs[key.SrcMAC] = struct{}{}
}
// If we know about the destination MAC, deliver it to the
// associated port.
if sender := fastdp.sendToMAC[key.DstMAC]; sender != nil {
return NewMultiFlowOp(false, odpEthernetFlowKey(key), sender(key, lock))
}
// Otherwise, it might be a real broadcast, or it might
// be for a MAC we don't know about yet. Either way, we'll
// broadcast it.
mfop := NewMultiFlowOp(false)
if (key.DstMAC[0] & 1) == 0 {
// Not a real broadcast, so don't create a flow rule.
// If we did, we'd need to delete the flows every time
// we learned a new MAC address, or have a more
// complicated selective invalidation scheme.
log.Debug("fastdp: unknown dst", ingress, key)
mfop.Add(vetoFlowCreationFlowOp{})
} else {
// A real broadcast
log.Debug("fastdp: broadcast", ingress, key)
mfop.Add(odpEthernetFlowKey(key))
}
// Send to all ports except the one it came in on. The
// sendToPort map is immutable, so it is safe to iterate over
// it even though the sender functions can drop the
// fastDatapathLock
for id, sender := range fastdp.sendToPort {
if id != ingress {
mfop.Add(sender(key, lock))
}
}
return mfop
}
// Overlay bits
type fastDatapathOverlay struct {
*FastDatapath
}
func (fastdp *FastDatapath) Overlay() NetworkOverlay {
return fastDatapathOverlay{fastdp}
}
func (fastdp fastDatapathOverlay) InvalidateRoutes() {
log.Debug("InvalidateRoutes")
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
checkWarn(fastdp.deleteFlows())
}
func (fastdp fastDatapathOverlay) InvalidateShortIDs() {
log.Debug("InvalidateShortIDs")
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
checkWarn(fastdp.deleteFlows())
}
func (fastdp fastDatapathOverlay) Stop() {
if fastdp.ipsec != nil {
if err := fastdp.ipsec.Flush(true); err != nil {
log.Errorf("ipsec flush failed: %s", err)
}
}
}
func (fastDatapathOverlay) AddFeaturesTo(features map[string]string) {
// Nothing needed. Fast datapath support is indicated through
// OverlaySwitch.
}
type FastDPStatus struct {
Vports []VportStatus
Flows []FlowStatus
}
type FlowStatus odp.FlowInfo
func (flowStatus *FlowStatus) MarshalJSON() ([]byte, error) {
type jsonFlowStatus struct {
FlowKeys []string
Actions []string
Packets uint64
Bytes uint64
Used uint64
}
flowKeys := make([]string, 0, len(flowStatus.FlowKeys))
for _, flowKey := range flowStatus.FlowKeys {
if !flowKey.Ignored() {
flowKeys = append(flowKeys, fmt.Sprint(flowKey))
}
}
actions := make([]string, 0, len(flowStatus.Actions))
for _, action := range flowStatus.Actions {
actions = append(actions, fmt.Sprint(action))
}
return json.Marshal(&jsonFlowStatus{flowKeys, actions, flowStatus.Packets, flowStatus.Bytes, flowStatus.Used})
}
type VportStatus odp.Vport
func (vport *VportStatus) MarshalJSON() ([]byte, error) {
type jsonVportStatus struct {
ID odp.VportID
Name string
TypeName string
}
return json.Marshal(&jsonVportStatus{vport.ID, vport.Spec.Name(), vport.Spec.TypeName()})
}
func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
lock := fastdp.startLock()
defer lock.unlock()
vports, err := fastdp.dp.EnumerateVports()
checkWarn(err)
vportStatuses := make([]VportStatus, 0, len(vports))
for _, vport := range vports {
vportStatuses = append(vportStatuses, VportStatus(vport))
}
flows, err := fastdp.dp.EnumerateFlows()
checkWarn(err)
flowStatuses := make([]FlowStatus, 0, len(flows))
for _, flow := range flows {
flowStatuses = append(flowStatuses, FlowStatus(flow))
}
return FastDPStatus{
vportStatuses,
flowStatuses,
}
}
type FastDPMetrics struct {
Flows int
TotalPackets uint64
TotalBytes uint64
}
func (s FastDPStatus) Metrics() interface{} {
var m FastDPMetrics
m.Flows = len(s.Flows)
for _, flow := range s.Flows {
m.TotalPackets += flow.Packets
m.TotalBytes += flow.Bytes
}
return &m
}
func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
if fastdp.overlayConsumer != nil {
return fmt.Errorf("FastDatapath already has an OverlayConsumer")
}
fastdp.localPeer = localPeer
fastdp.peers = peers
fastdp.overlayConsumer = consumer
return nil
}
func (fastdp *FastDatapath) getVxlanVportIDHarder(udpPort int, retries int, duration time.Duration) (odp.VportID, error) {
var vxlanVportID odp.VportID
var err error
for try := 0; try < retries; try++ {
vxlanVportID, err = fastdp.getVxlanVportID(udpPort)
if err == nil || err != odp.NetlinkError(syscall.EADDRINUSE) {
return vxlanVportID, err
}
log.Warning("Address already in use creating vxlan vport ", udpPort, " - retrying")
time.Sleep(duration)
}
return 0, err
}
func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
if vxlanVportID, present := fastdp.vxlanUDPPorts[udpPort]; present {
return vxlanVportID, nil
}
name := fmt.Sprintf("vxlan-%d", udpPort)
vxlanVportID, err := fastdp.dp.CreateVport(
odp.NewVxlanVportSpec(name, uint16(udpPort)))
if err != nil {
return 0, err
}
// If a netdev for the vxlan vport exists, we need to do an extra check
// to bypass the kernel bug which makes the vxlan creation to complete
// successfully regardless whether there were any errors when binding
// to the given UDP port.
if link, err := netlink.LinkByName(name); err == nil {
if link.Attrs().Flags&net.FlagUp == 0 {
// The netdev interface is down, so most likely bringing it up
// has failed due to the UDP port being in use.
if err := fastdp.dp.DeleteVport(vxlanVportID); err != nil {
log.Warning("Unable to remove vxlan vport %d: %s", vxlanVportID, err)
}
return 0, odp.NetlinkError(syscall.EADDRINUSE)
}
}
fastdp.vxlanUDPPorts[udpPort] = vxlanVportID
fastdp.vxlanVportIDs[vxlanVportID] = struct{}{}
fastdp.missHandlers[vxlanVportID] = func(fks odp.FlowKeys, lock *fastDatapathLock) FlowOp {
log.Debug("ODP miss: ", fks, " on port ", vxlanVportID)
tunnel := fks[odp.OVS_KEY_ATTR_TUNNEL].(odp.TunnelFlowKey)
tunKey := tunnel.Key()
lock.relock()
consumer := fastdp.overlayConsumer
if consumer == nil {
return vetoFlowCreationFlowOp{}
}
srcPeer, dstPeer := fastdp.extractPeers(tunKey.TunnelId)
if srcPeer == nil || dstPeer == nil {
return vetoFlowCreationFlowOp{}
}
lock.unlock()
pk := flowKeysToPacketKey(fks)
var zeroMAC MAC
if pk.SrcMAC == zeroMAC && pk.DstMAC == zeroMAC {
return vxlanSpecialPacketFlowOp{
fastdp: fastdp,
srcPeer: srcPeer,
sender: &net.UDPAddr{
IP: net.IP(tunKey.Ipv4Src[:]),
Port: udpPort,
},
}
}
key := ForwardPacketKey{
SrcPeer: srcPeer,
DstPeer: dstPeer,
PacketKey: pk,
}
var tunnelFlowKey odp.TunnelFlowKey
tunnelFlowKey.SetTunnelId(tunKey.TunnelId)
tunnelFlowKey.SetIpv4Src(tunKey.Ipv4Src)
tunnelFlowKey.SetIpv4Dst(tunKey.Ipv4Dst)
return NewMultiFlowOp(false, odpFlowKey(tunnelFlowKey), consumer(key))
}
return vxlanVportID, nil
}
func (fastdp *FastDatapath) extractPeers(tunnelID [8]byte) (*mesh.Peer, *mesh.Peer) {
vni := binary.BigEndian.Uint64(tunnelID[:])
srcPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID(vni & 0xfff))
dstPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID((vni >> 12) & 0xfff))
return srcPeer, dstPeer
}
type vxlanSpecialPacketFlowOp struct {
NonDiscardingFlowOp
fastdp *FastDatapath
srcPeer *mesh.Peer
sender *net.UDPAddr
}
func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
op.fastdp.lock.Lock()
fwd := op.fastdp.forwarders[op.srcPeer.Name]
op.fastdp.lock.Unlock()
if fwd != nil && dec.IsSpecial() {
fwd.handleVxlanSpecialPacket(frame, op.sender)
}
}
type fastDatapathForwarder struct {
fastdp *FastDatapath
remotePeer *mesh.Peer
localIP [4]byte
sendControlMsg func(byte, []byte) error
connUID uint64
vxlanVportID odp.VportID
sessionKey *[32]byte
isEncrypted bool
isOutboundIPSecEstablished bool
lock sync.RWMutex
confirmed bool
remoteAddr *net.UDPAddr
heartbeatInterval time.Duration
heartbeatTimer *time.Timer // for sending
heartbeatTimeout *time.Timer // for receiving
ackedHeartbeat bool
stopChan chan struct{}
stopped bool
healthy bool
established bool
establishedChan chan struct{}
errorChan chan error
healthChan chan bool
}
func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
vxlanVportID := fastdp.mainVxlanVportID
vxlanUDPPort := fastdp.mainVxlanUDPPort
remoteAddr := makeUDPAddr(params.RemoteAddr)
if params.Outbound {
var err error
// The provided address contains the main weave port
// number to connect to. We need to derive the vxlan
// port number from that.
vxlanRemoteAddr := *remoteAddr
vxlanRemoteAddr.Port++
remoteAddr = &vxlanRemoteAddr
vxlanUDPPort = remoteAddr.Port
vxlanVportID, err = fastdp.getVxlanVportID(vxlanUDPPort)
if err != nil {
return nil, err
}
} else {
remoteAddr.Port = vxlanUDPPort
}
localIP, err := ipv4Bytes(params.LocalAddr.IP)
if err != nil {
return nil, err
}
fwd := &fastDatapathForwarder{
fastdp: fastdp.FastDatapath,
remotePeer: params.RemotePeer,
localIP: localIP,
sendControlMsg: params.SendControlMessage,
connUID: params.ConnUID,
vxlanVportID: vxlanVportID,
sessionKey: params.SessionKey,
healthy: true,
remoteAddr: remoteAddr,
heartbeatInterval: FastHeartbeat,
stopChan: make(chan struct{}),
establishedChan: make(chan struct{}),
errorChan: make(chan error, 1),
healthChan: make(chan bool),
}
return fwd, nil
}
func ipv4Bytes(ip net.IP) (res [4]byte, err error) {
ipv4 := ip.To4()
if ipv4 != nil {
copy(res[:], ipv4)
} else {
err = fmt.Errorf("IP address %s is not IPv4", ip)
}
return
}
func (fwd *fastDatapathForwarder) logPrefix() string {
return fmt.Sprintf("fastdp ->[%s|%s]: ", fwd.remoteAddr, fwd.remotePeer)
}
func (fwd *fastDatapathForwarder) Confirm() {
fwd.lock.Lock()
defer fwd.lock.Unlock()
if fwd.confirmed {
log.Fatal(fwd.logPrefix(), "already confirmed")
}
if fwd.fastdp.ipsec != nil && fwd.sessionKey != nil {
fwd.isEncrypted = true
log.Info("Setting up IPsec between ", fwd.fastdp.localPeer, " and ", fwd.remotePeer)
err := fwd.fastdp.ipsec.InitSALocal(
fwd.fastdp.localPeer.Name, fwd.remotePeer.Name, fwd.connUID,
net.IP(fwd.localIP[:]), fwd.remoteAddr.IP,
fwd.remoteAddr.Port,
fwd.sessionKey,
func(msg []byte) error {
return fwd.sendControlMsg(FastDatapathCryptoInitSARemote, msg)
},
)
if err != nil {
log.Error(fwd.logPrefix(), "ipsec init SA local failed: ", err)
fwd.handleError(err)
return
}
}
log.Debug(fwd.logPrefix(), "confirmed")
fwd.fastdp.addForwarder(fwd.remotePeer.Name, fwd)
fwd.confirmed = true
if fwd.remoteAddr != nil && (!fwd.isEncrypted || fwd.isOutboundIPSecEstablished) {
// have the goroutine send a heartbeat straight away
fwd.heartbeatTimer = time.NewTimer(0)
} else {
// we'll reset the timer when we learn the remote ip
fwd.heartbeatTimer = time.NewTimer(MaxDuration)
}
fwd.heartbeatTimeout = time.NewTimer(HeartbeatTimeout)
go fwd.doHeartbeats()
}
func (fwd *fastDatapathForwarder) EstablishedChannel() <-chan struct{} {
return fwd.establishedChan
}
func (fwd *fastDatapathForwarder) ErrorChannel() <-chan error {
return fwd.errorChan
}
func (fwd *fastDatapathForwarder) HealthChannel() <-chan bool {
return fwd.healthChan
}
func (fwd *fastDatapathForwarder) doHeartbeats() {
var err error
for err == nil {
select {
case <-fwd.heartbeatTimer.C:
if fwd.confirmed {
log.Debug(fwd.logPrefix(), "sending Heartbeat to peer")
fwd.sendHeartbeat()
}
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
case <-fwd.heartbeatTimeout.C:
log.Debug(fwd.logPrefix(), "missed Heartbeat from peer, marking fastdp forwarder as un-healthy")
// treat missed heartbeat as transient error. Indicate to overlay forwarder
// that Forwarder is un-healthy so it can pick next best forwarder
fwd.healthChan <- false
// switch from fast-heartbeat to slow-heartbeat to avoid aggressive heartbeats
// when there is no estbalished session with peer yet
if fwd.heartbeatInterval != SlowHeartbeat {
fwd.heartbeatInterval = SlowHeartbeat
if fwd.heartbeatTimer != nil {
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
}
}
if fwd.healthy {
fwd.healthy = false
}
case <-fwd.stopChan:
return
}
}
fwd.lock.Lock()
defer fwd.lock.Unlock()
fwd.handleError(err)
}
// Handle an error which leads to notifying the listener and
// termination of the forwarder
func (fwd *fastDatapathForwarder) handleError(err error) {
if err == nil {
return
}
select {
case fwd.errorChan <- err:
default:
}
// stop the heartbeat goroutine
if !fwd.stopped {
fwd.stopped = true
close(fwd.stopChan)
}
}
func (fwd *fastDatapathForwarder) sendHeartbeat() {
fwd.lock.RLock()
// the heartbeat payload consists of the 64-bit connection uid
// followed by the 16-bit packet size.
buf := make([]byte, EthernetOverhead+fwd.fastdp.iface.MTU)
binary.BigEndian.PutUint64(buf[EthernetOverhead:], fwd.connUID)
binary.BigEndian.PutUint16(buf[EthernetOverhead+8:], uint16(len(buf)))
dec := NewEthernetDecoder()
dec.DecodeLayers(buf)
pk := ForwardPacketKey{
PacketKey: dec.PacketKey(),
SrcPeer: fwd.fastdp.localPeer,
DstPeer: fwd.remotePeer,
}
fwd.lock.RUnlock()
if fop := fwd.Forward(pk); fop != nil {
fop.Process(buf, dec, false)
}
}
const (
FastDatapathHeartbeatAck = iota
FastDatapathCryptoInitSARemote
)
func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte, sender *net.UDPAddr) {
fwd.lock.Lock()
defer fwd.lock.Unlock()
log.Debug(fwd.logPrefix(), "handleVxlanSpecialPacket")
// the only special packet type is a heartbeat
if len(frame) < EthernetOverhead+10 {
log.Warning(fwd.logPrefix(), "short vxlan special packet: ", len(frame), " bytes")
return
}
if binary.BigEndian.Uint64(frame[EthernetOverhead:]) != fwd.connUID ||
uint16(len(frame)) != binary.BigEndian.Uint16(frame[EthernetOverhead+8:]) {
return
}
if fwd.remoteAddr == nil {
fwd.remoteAddr = sender
if fwd.confirmed {
fwd.heartbeatTimer.Reset(0)
}
} else if !udpAddrsEqual(fwd.remoteAddr, sender) {
log.Info(fwd.logPrefix(), "Peer IP address changed to ", sender)
fwd.remoteAddr = sender
}
if !fwd.ackedHeartbeat {
fwd.ackedHeartbeat = true
log.Debug(fwd.logPrefix(), "Ack Heartbeat from peer")
fwd.handleError(fwd.sendControlMsg(FastDatapathHeartbeatAck, nil))
} else {
log.Debug(fwd.logPrefix(), "Got Heartbeat Ack from peer")
}
// we can receive a heartbeat before Confirm() has set up
// heartbeatTimeout
if fwd.heartbeatTimeout != nil {
fwd.heartbeatTimeout.Reset(HeartbeatTimeout)
if !fwd.healthy {
// If fastdp was marked as unhealthy earlier due to missed heartbeat then indicate to
// overlay forwarder that Forwarder is healthy now so it can pick next best forwarder
log.Debug(fwd.logPrefix(), "got Heartbeat from peer, marking fastdp forwarder as healthy")
fwd.healthy = true
fwd.healthChan <- true
}
}
}
func (fwd *fastDatapathForwarder) ControlMessage(tag byte, msg []byte) {
fwd.lock.Lock()
defer fwd.lock.Unlock()
switch tag {
case FastDatapathHeartbeatAck:
fwd.handleHeartbeatAck()
case FastDatapathCryptoInitSARemote:
fwd.handleCryptoInitSARemote(msg)
default:
log.Info(fwd.logPrefix(), "Ignoring unknown control message: ", tag)
}
}
func (fwd *fastDatapathForwarder) Attrs() map[string]interface{} {
return map[string]interface{}{"name": "fastdp", "mtu": fwd.fastdp.iface.MTU}
}
func (fwd *fastDatapathForwarder) handleHeartbeatAck() {
log.Debug(fwd.logPrefix(), "handleHeartbeatAck")
if !fwd.established {
close(fwd.establishedChan)
fwd.established = true
}
if fwd.heartbeatInterval != SlowHeartbeat {
fwd.heartbeatInterval = SlowHeartbeat
if fwd.heartbeatTimer != nil {
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
}
}
}
func (fwd *fastDatapathForwarder) handleCryptoInitSARemote(msg []byte) {
log.Info(fwd.logPrefix(), "IPSec init SA remote")
err := fwd.fastdp.ipsec.InitSARemote(
msg,
fwd.fastdp.localPeer.Name, fwd.remotePeer.Name, fwd.connUID,
net.IP(fwd.localIP[:]), fwd.remoteAddr.IP, fwd.remoteAddr.Port,
fwd.sessionKey,
)
if err != nil {
log.Warning(fwd.logPrefix(), "IPSec init SA remote failed: ", err)
fwd.handleError(err)
return
}
// FastDatapathCryptoInitSARemote can be received before Confirm'ing
// connection, thus before InitSALocal.
if fwd.confirmed && !fwd.isOutboundIPSecEstablished {
fwd.isOutboundIPSecEstablished = true
fwd.heartbeatTimer.Reset(0)
}
}
func (fwd *fastDatapathForwarder) Forward(key ForwardPacketKey) FlowOp {
if !key.SrcPeer.HasShortID || !key.DstPeer.HasShortID {
return nil
}
fwd.lock.RLock()
defer fwd.lock.RUnlock()
if fwd.remoteAddr == nil {
// Returning a DiscardingFlowOp would discard the
// packet, but also result in a flow rule, which we
// would have to invalidate when we learn the remote
// IP. So for now, just prevent flows.
return vetoFlowCreationFlowOp{}
}
remoteIP, err := ipv4Bytes(fwd.remoteAddr.IP)
if err != nil {
log.Error(err)
return DiscardingFlowOp{}
}
var sta odp.SetTunnelAction
sta.SetTunnelId(tunnelIDFor(key))
sta.SetIpv4Src(fwd.localIP)
sta.SetIpv4Dst(remoteIP)
sta.SetTos(0)
sta.SetTtl(64)
sta.SetDf(true)
sta.SetCsum(false)
return fwd.fastdp.odpActions(sta, odp.NewOutputAction(fwd.vxlanVportID))
}
func tunnelIDFor(key ForwardPacketKey) (tunnelID [8]byte) {
src := uint64(key.SrcPeer.ShortID)
dst := uint64(key.DstPeer.ShortID)
binary.BigEndian.PutUint64(tunnelID[:], src|dst<<12)
return
}
func (fwd *fastDatapathForwarder) Stop() {
// Might be nice to delete all the relevant flows here, but we
// can just let them expire.
fwd.fastdp.removeForwarder(fwd.remotePeer.Name, fwd)
fwd.lock.Lock()
defer fwd.lock.Unlock()
fwd.sendControlMsg = func(byte, []byte) error { return nil }
if fwd.isEncrypted {
localIP := net.IP(fwd.localIP[:])
log.Info("Destroying IPsec between ", fwd.fastdp.localPeer, " and ", fwd.remotePeer)
err := fwd.fastdp.ipsec.Destroy(
fwd.fastdp.localPeer.Name, fwd.remotePeer.Name, fwd.connUID,
localIP, fwd.remoteAddr.IP, fwd.remoteAddr.Port,
)
if err != nil {
log.Errorf("ipsec destroy failed: %s", err)
}
}
// stop the heartbeat goroutine
if !fwd.stopped {
fwd.stopped = true
close(fwd.stopChan)
}
}
func (fastdp *FastDatapath) addForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
// We shouldn't have two confirmed forwarders to the same
// remotePeer, due to the checks in LocalPeer AddConnection.
fastdp.forwarders[peer] = fwd
}
func (fastdp *FastDatapath) removeForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
if fastdp.forwarders[peer] == fwd {
delete(fastdp.forwarders, peer)
}
}
func (fastdp *FastDatapath) deleteFlows() error {
fastdp.deleteFlowsCount++
flows, err := fastdp.dp.EnumerateFlows()
if err != nil {
return err
}
for _, flow := range flows {
err = fastdp.dp.DeleteFlow(flow.FlowKeys)
if err != nil && !odp.IsNoSuchFlowError(err) {
return err
}
}
return nil
}
func (fastdp *FastDatapath) deleteVxlanVports() error {
vports, err := fastdp.dp.EnumerateVports()
if err != nil {
return err
}
for _, vport := range vports {
if vport.Spec.TypeName() != "vxlan" {
continue
}
err = fastdp.dp.DeleteVport(vport.ID)
if err != nil && !odp.IsNoSuchVportError(err) {
return err
}
}
return nil
}
func (fastdp *FastDatapath) run() {
expireMACsCh := time.Tick(10 * time.Minute)
expireFlowsCh := time.Tick(5 * time.Minute)
for {
select {
case <-expireMACsCh:
fastdp.expireMACs()
case <-expireFlowsCh:
fastdp.expireFlows()
}
}
}
func (fastdp *FastDatapath) expireMACs() {
lock := fastdp.startLock()
defer lock.unlock()
for mac := range fastdp.sendToMAC {
if _, present := fastdp.seenMACs[mac]; !present {
delete(fastdp.sendToMAC, mac)
}
}
fastdp.seenMACs = make(map[MAC]struct{})
}
func (fastdp *FastDatapath) expireFlows() {
lock := fastdp.startLock()
defer lock.unlock()
flows, err := fastdp.dp.EnumerateFlows()
checkWarn(err)
for _, flow := range flows {
if flow.Used == 0 {
log.Debug("Expiring flow ", flow.FlowSpec)
err = fastdp.dp.DeleteFlow(flow.FlowKeys)
} else {
fastdp.touchFlow(flow.FlowKeys, &lock)
err = fastdp.dp.ClearFlow(flow.FlowSpec)
}
if err != nil && !odp.IsNoSuchFlowError(err) {
log.Warn(err)
}
}
}
// The router needs to know which flows are active in order to
// maintain its MAC->peer table. We do this by querying the router
// without an actual packet being involved. Maybe it's
// worth devising a more unified approach in the future.
func (fastdp *FastDatapath) touchFlow(fks odp.FlowKeys, lock *fastDatapathLock) {
// All the flows we create should have an ingress key, but we
// check here just in case we encounter one from somewhere
// else.
ingressKey, present := fks[odp.OVS_KEY_ATTR_IN_PORT]
if present {
ingress := ingressKey.(odp.InPortFlowKey).VportID()
handler := fastdp.getMissHandler(ingress)
if handler != nil {
handler(fks, lock)
lock.relock()
}
}
}
func (fastdp *FastDatapath) Error(err error, stopped bool) {
if stopped {
log.Fatal("Error while listeniing on ODP datapath: ", err)
}
log.Error("Error while listening on ODP datapath: ", err)
}
func (fastdp *FastDatapath) Miss(packet []byte, fks odp.FlowKeys) error {
ingress := fks[odp.OVS_KEY_ATTR_IN_PORT].(odp.InPortFlowKey).VportID()
lock := fastdp.startLock()
defer lock.unlock()
fastdp.missCount++
handler := fastdp.getMissHandler(ingress)
if handler == nil {
log.Debug("ODP miss (no handler): ", fks, " on port ", ingress)
return nil
}
// Always include the ingress vport in the flow key. While
// this is not strictly necessary in some cases (e.g. for
// delivery to a local netdev based on the dest MAC),
// including the ingress in every flow makes things simpler
// in touchFlow.
mfop := NewMultiFlowOp(false, handler(fks, &lock), odpFlowKey(odp.NewInPortFlowKey(ingress)))
fastdp.send(mfop, packet, &lock)
return nil
}
func (fastdp *FastDatapath) getMissHandler(ingress odp.VportID) missHandler {
handler := fastdp.missHandlers[ingress]
if handler == nil {
vport, err := fastdp.dp.LookupVport(ingress)
if err != nil {
log.Error(err)
return nil
}
fastdp.makeBridgeVport(vport)
}
return handler
}
func (fastdp *FastDatapath) VportCreated(dpid odp.DatapathID, vport odp.Vport) error {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
if _, present := fastdp.missHandlers[vport.ID]; !present {
fastdp.makeBridgeVport(vport)
}
return nil
}
func (fastdp *FastDatapath) VportDeleted(dpid odp.DatapathID, vport odp.Vport) error {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
// there might be flow rules that still refer to the id of
// this vport. But we just allow them to expire. Unless we
// want prompt migration of MAC addresses, that should be
// fine.
delete(fastdp.missHandlers, vport.ID)
fastdp.deleteSendToPort(bridgePortID{vport: vport.ID})
return nil
}
func (fastdp *FastDatapath) makeBridgeVport(vport odp.Vport) {
// Set up a bridge port for netdev and internal vports. vxlan
// vports are handled separately, as they do not correspond to
// bridge ports (we set up the miss handler for them in
// getVxlanVportID).
typ := vport.Spec.TypeName()
if typ != "netdev" && typ != "internal" {
return
}
vportID := vport.ID
// Sending to the bridge port outputs on the vport:
fastdp.addSendToPort(bridgePortID{vport: vportID},
func(_ PacketKey, _ *fastDatapathLock) FlowOp {
return fastdp.odpActions(odp.NewOutputAction(vportID))
})
// Delete flows, in order to recalculate flows for broadcasts
// on the bridge.
checkWarn(fastdp.deleteFlows())
// Packets coming from the netdev are processed by the bridge
fastdp.missHandlers[vportID] = func(flowKeys odp.FlowKeys, lock *fastDatapathLock) FlowOp {
return fastdp.bridge(bridgePortID{vport: vportID}, flowKeysToPacketKey(flowKeys), lock)
}
}
func flowKeysToPacketKey(fks odp.FlowKeys) PacketKey {
eth := fks[odp.OVS_KEY_ATTR_ETHERNET].(odp.EthernetFlowKey).Key()
return PacketKey{SrcMAC: eth.EthSrc, DstMAC: eth.EthDst}
}
// The sendToPort map is read-only, so this method does the copy in
// order to add an entry.
func (fastdp *FastDatapath) addSendToPort(portID bridgePortID, sender bridgeSender) {
sendToPort := map[bridgePortID]bridgeSender{portID: sender}
for id, sender := range fastdp.sendToPort {
sendToPort[id] = sender
}
fastdp.sendToPort = sendToPort
}
func (fastdp *FastDatapath) deleteSendToPort(portID bridgePortID) {
sendToPort := make(map[bridgePortID]bridgeSender)
for id, sender := range fastdp.sendToPort {
if id != portID {
sendToPort[id] = sender
}
}
fastdp.sendToPort = sendToPort
}
// Send a packet, creating a corresponding ODP flow rule if possible
func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, lock *fastDatapathLock) {
// Gather the actions from actionFlowOps, execute any others
var dec *EthernetDecoder
flow := odp.NewFlowSpec()
createFlow := true
for _, xfop := range FlattenFlowOp(fops) {
switch fop := xfop.(type) {
case interface {
updateFlowSpec(*odp.FlowSpec)
}:
fop.updateFlowSpec(&flow)
case vetoFlowCreationFlowOp:
createFlow = false
default:
if xfop.Discards() {
continue
}
// A foreign FlowOp (e.g. a sleeve forwarding
// FlowOp), so send the packet through the
// FlowOp interface, decoding the packet
// lazily.
if dec == nil {
dec = fastdp.takeDecoder(lock)
dec.DecodeLayers(frame)
// If we are sending the packet
// through the FlowOp interface, we
// mustn't create a flow, as that
// could prevent the proper handling
// of similar packets in the future.
createFlow = false
}
if len(dec.decoded) != 0 {
lock.unlock()
fop.Process(frame, dec, false)
}
}
}
if fastdp.isHairpinFlow(&flow) {
log.Warning("Vetoed installation of hairpin flow ", flow)
return
}
if dec != nil {
// put the decoder back
lock.relock()
fastdp.dec = dec
}
if len(flow.Actions) != 0 {
lock.relock()
checkWarn(fastdp.dp.Execute(frame, nil, flow.Actions))
}
if createFlow {
lock.relock()
// if the fastdp's deleteFlowsCount changed since we
// initially locked it, then we might have created a
// flow on the basis of stale information. It's fine
// to handle one packet like that, but it would be bad
// to introduce a stale flow.
if lock.deleteFlowsCount == fastdp.deleteFlowsCount {
log.Debug("Creating ODP flow ", flow)
checkWarn(fastdp.dp.CreateFlow(flow))
}
}
}
// Get the EthernetDecoder from the singleton pool
func (fastdp *FastDatapath) takeDecoder(lock *fastDatapathLock) *EthernetDecoder {
lock.relock()
dec := fastdp.dec
if dec == nil {
dec = NewEthernetDecoder()
} else {
fastdp.dec = nil
}
return dec
}
// A isHairpinFlow checks whether the flow is created due to enabled hairpin
// mode on the weave bridge port which attaches the datapath. Such flow is
// identified by either:
//
// * in_port == out_port, where in_port is non-vxlan vport;
// * a packet is sent back to a vxlan tunnel it has been received from and
// the tunnel id is either the same or dstPeer and srcPeer are reversed.
func (fastdp *FastDatapath) isHairpinFlow(flow *odp.FlowSpec) bool {
var (
vxlanKey odp.TunnelAttrs
inVport odp.VportID
inVxlan bool
)
for _, key := range flow.FlowKeys {
switch k := key.(type) {
case odp.InPortFlowKey:
inVport = k.VportID()
case odp.TunnelFlowKey:
inVxlan = true
vxlanKey = k.Key()
}
}
for _, action := range flow.Actions {
switch a := action.(type) {
case odp.SetTunnelAction:
if inVxlan && a.TunnelAttrs.TunnelId == vxlanKey.TunnelId &&
a.TunnelAttrs.Ipv4Src == vxlanKey.Ipv4Dst &&
a.TunnelAttrs.Ipv4Dst == vxlanKey.Ipv4Src {
return true
}
case odp.OutputAction:
if a.VportID() == inVport {
if _, ok := fastdp.vxlanVportIDs[a.VportID()]; !ok {
return true
}
}
}
}
return false
}
type odpActionsFlowOp struct {
NonDiscardingFlowOp
fastdp *FastDatapath
actions []odp.Action
}
func (fastdp *FastDatapath) odpActions(actions ...odp.Action) FlowOp {
return odpActionsFlowOp{
fastdp: fastdp,
actions: actions,
}
}
func (fop odpActionsFlowOp) updateFlowSpec(flow *odp.FlowSpec) {
flow.AddActions(fop.actions)
}
func (fop odpActionsFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
fastdp := fop.fastdp
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
checkWarn(fastdp.dp.Execute(frame, nil, fop.actions))
}
// A vetoFlowCreationFlowOp flags that no flow should be created
type vetoFlowCreationFlowOp struct {
DiscardingFlowOp
}
// A odpFlowKeyFlowOp adds a FlowKey to the resulting flow
type odpFlowKeyFlowOp struct {
DiscardingFlowOp
key odp.FlowKey
}
func odpFlowKey(key odp.FlowKey) FlowOp {
return odpFlowKeyFlowOp{key: key}
}
func (fop odpFlowKeyFlowOp) updateFlowSpec(flow *odp.FlowSpec) {
flow.AddKey(fop.key)
}
func odpEthernetFlowKey(key PacketKey) FlowOp {
fk := odp.NewEthernetFlowKey()
fk.SetEthSrc(key.SrcMAC)
fk.SetEthDst(key.DstMAC)
return odpFlowKeyFlowOp{key: fk}
}