From 0ae13a2650582bfce2dfdff029cd03f56a5009f4 Mon Sep 17 00:00:00 2001 From: Martynas Pumputis Date: Thu, 24 Nov 2016 18:24:16 +0000 Subject: [PATCH] Detect hairpin flows The introduced "isHairpinFlow" detects whether a flow has been created due to enabled hairpin mode. --- router/fastdp.go | 60 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/router/fastdp.go b/router/fastdp.go index 6627cd4e..33e0cd41 100644 --- a/router/fastdp.go +++ b/router/fastdp.go @@ -50,7 +50,8 @@ type FastDatapath struct { seenMACs map[MAC]struct{} // vxlan vports associated with the given UDP ports - vxlanVportIDs map[int]odp.VportID + vxlanUDPPorts map[int]odp.VportID + vxlanVportIDs map[odp.VportID]struct{} mainVxlanVportID odp.VportID // A singleton pool for the occasions when we need to decode @@ -87,7 +88,8 @@ func NewFastDatapath(iface *net.Interface, port int) (*FastDatapath, error) { sendToPort: nil, sendToMAC: make(map[MAC]bridgeSender), seenMACs: make(map[MAC]struct{}), - vxlanVportIDs: make(map[int]odp.VportID), + vxlanUDPPorts: make(map[int]odp.VportID), + vxlanVportIDs: make(map[odp.VportID]struct{}), forwarders: make(map[mesh.PeerName]*fastDatapathForwarder), } @@ -433,7 +435,7 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) { fastdp.lock.Lock() defer fastdp.lock.Unlock() - if vxlanVportID, present := fastdp.vxlanVportIDs[udpPort]; present { + if vxlanVportID, present := fastdp.vxlanUDPPorts[udpPort]; present { return vxlanVportID, nil } @@ -459,7 +461,8 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) { } } - fastdp.vxlanVportIDs[udpPort] = vxlanVportID + 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) @@ -1134,6 +1137,11 @@ func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, lock *fastDatapathLo } } + if fastdp.isHairpinFlow(&flow) { + log.Error("Vetoed installation of hairpin flow ", flow) + return + } + if dec != nil { // put the decoder back lock.relock() @@ -1171,6 +1179,50 @@ func (fastdp *FastDatapath) takeDecoder(lock *fastDatapathLock) *EthernetDecoder 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