1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #1935 from fcrisciani/overlay-setmatrix

Overlay fix for transient IP reuse
This commit is contained in:
Madhu Venugopal 2017-10-03 07:38:44 -07:00 committed by GitHub
commit 44dbb26172
9 changed files with 221 additions and 161 deletions

View file

@ -21,7 +21,6 @@ import (
const ( const (
r = 0xD0C4E3 r = 0xD0C4E3
timeout = 30
pktExpansion = 26 // SPI(4) + SeqN(4) + IV(8) + PadLength(1) + NextHeader(1) + ICV(8) pktExpansion = 26 // SPI(4) + SeqN(4) + IV(8) + PadLength(1) + NextHeader(1) + ICV(8)
) )

View file

@ -68,7 +68,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
ep.ifName = containerIfName ep.ifName = containerIfName
if err := d.writeEndpointToStore(ep); err != nil { if err = d.writeEndpointToStore(ep); err != nil {
return fmt.Errorf("failed to update overlay endpoint %s to local data store: %v", ep.id[0:7], err) return fmt.Errorf("failed to update overlay endpoint %s to local data store: %v", ep.id[0:7], err)
} }
@ -86,7 +86,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
return err return err
} }
if err := sbox.AddInterface(overlayIfName, "veth", if err = sbox.AddInterface(overlayIfName, "veth",
sbox.InterfaceOptions().Master(s.brName)); err != nil { sbox.InterfaceOptions().Master(s.brName)); err != nil {
return fmt.Errorf("could not add veth pair inside the network sandbox: %v", err) return fmt.Errorf("could not add veth pair inside the network sandbox: %v", err)
} }
@ -100,7 +100,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
return err return err
} }
if err := nlh.LinkSetHardwareAddr(veth, ep.mac); err != nil { if err = nlh.LinkSetHardwareAddr(veth, ep.mac); err != nil {
return fmt.Errorf("could not set mac address (%v) to the container interface: %v", ep.mac, err) return fmt.Errorf("could not set mac address (%v) to the container interface: %v", ep.mac, err)
} }
@ -108,7 +108,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
if sub == s { if sub == s {
continue continue
} }
if err := jinfo.AddStaticRoute(sub.subnetIP, types.NEXTHOP, s.gwIP.IP); err != nil { if err = jinfo.AddStaticRoute(sub.subnetIP, types.NEXTHOP, s.gwIP.IP); err != nil {
logrus.Errorf("Adding subnet %s static route in network %q failed\n", s.subnetIP, n.id) logrus.Errorf("Adding subnet %s static route in network %q failed\n", s.subnetIP, n.id)
} }
} }
@ -122,7 +122,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
d.peerAdd(nid, eid, ep.addr.IP, ep.addr.Mask, ep.mac, net.ParseIP(d.advertiseAddress), false, false, true) d.peerAdd(nid, eid, ep.addr.IP, ep.addr.Mask, ep.mac, net.ParseIP(d.advertiseAddress), false, false, true)
if err := d.checkEncryption(nid, nil, n.vxlanID(s), true, true); err != nil { if err = d.checkEncryption(nid, nil, n.vxlanID(s), true, true); err != nil {
logrus.Warn(err) logrus.Warn(err)
} }
@ -200,7 +200,7 @@ func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key stri
} }
if etype == driverapi.Delete { if etype == driverapi.Delete {
d.peerDelete(nid, eid, addr.IP, addr.Mask, mac, vtep) d.peerDelete(nid, eid, addr.IP, addr.Mask, mac, vtep, false)
return return
} }
@ -232,11 +232,9 @@ func (d *driver) Leave(nid, eid string) error {
} }
} }
n.leaveSandbox() d.peerDelete(nid, eid, ep.addr.IP, ep.addr.Mask, ep.mac, net.ParseIP(d.advertiseAddress), true)
if err := d.checkEncryption(nid, nil, 0, true, false); err != nil { n.leaveSandbox()
logrus.Warn(err)
}
return nil return nil
} }

View file

@ -251,8 +251,9 @@ func (d *driver) DeleteNetwork(nid string) error {
if err := d.deleteEndpointFromStore(ep); err != nil { if err := d.deleteEndpointFromStore(ep); err != nil {
logrus.Warnf("Failed to delete overlay endpoint %s from local store: %v", ep.id[0:7], err) logrus.Warnf("Failed to delete overlay endpoint %s from local store: %v", ep.id[0:7], err)
} }
} }
// flush the peerDB entries
d.peerFlush(nid)
d.deleteNetwork(nid) d.deleteNetwork(nid)
vnis, err := n.releaseVxlanID() vnis, err := n.releaseVxlanID()
@ -505,11 +506,7 @@ func (n *network) restoreSubnetSandbox(s *subnet, brName, vxlanName string) erro
vxlanIfaceOption := make([]osl.IfaceOption, 1) vxlanIfaceOption := make([]osl.IfaceOption, 1)
vxlanIfaceOption = append(vxlanIfaceOption, sbox.InterfaceOptions().Master(brName)) vxlanIfaceOption = append(vxlanIfaceOption, sbox.InterfaceOptions().Master(brName))
Ifaces[vxlanName+"+vxlan"] = vxlanIfaceOption Ifaces[vxlanName+"+vxlan"] = vxlanIfaceOption
err = sbox.Restore(Ifaces, nil, nil, nil) return sbox.Restore(Ifaces, nil, nil, nil)
if err != nil {
return err
}
return nil
} }
func (n *network) setupSubnetSandbox(s *subnet, brName, vxlanName string) error { func (n *network) setupSubnetSandbox(s *subnet, brName, vxlanName string) error {
@ -760,58 +757,38 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket) {
continue continue
} }
logrus.Debugf("miss notification: dest IP %v, dest MAC %v", ip, mac)
if neigh.State&(netlink.NUD_STALE|netlink.NUD_INCOMPLETE) == 0 { if neigh.State&(netlink.NUD_STALE|netlink.NUD_INCOMPLETE) == 0 {
continue continue
} }
if n.driver.isSerfAlive() { if n.driver.isSerfAlive() {
logrus.Debugf("miss notification: dest IP %v, dest MAC %v", ip, mac)
mac, IPmask, vtep, err := n.driver.resolvePeer(n.id, ip) mac, IPmask, vtep, err := n.driver.resolvePeer(n.id, ip)
if err != nil { if err != nil {
logrus.Errorf("could not resolve peer %q: %v", ip, err) logrus.Errorf("could not resolve peer %q: %v", ip, err)
continue continue
} }
n.driver.peerAdd(n.id, "dummy", ip, IPmask, mac, vtep, l2Miss, l3Miss, false) n.driver.peerAdd(n.id, "dummy", ip, IPmask, mac, vtep, l2Miss, l3Miss, false)
} else { } else if l3Miss && time.Since(t) > time.Second {
// If the gc_thresh values are lower kernel might knock off the neighor entries. // All the local peers will trigger a miss notification but this one is expected and the local container will reply
// When we get a L3 miss check if its a valid peer and reprogram the neighbor // autonomously to the ARP request
// entry again. Rate limit it to once attempt every 500ms, just in case a faulty // In case the gc_thresh3 values is low kernel might reject new entries during peerAdd. This will trigger the following
// container sends a flood of packets to invalid peers // extra logs that will inform of the possible issue.
if !l3Miss { // Entries created would not be deleted see documentation http://man7.org/linux/man-pages/man7/arp.7.html:
continue // Entries which are marked as permanent are never deleted by the garbage-collector.
} // The time limit here is to guarantee that the dbSearch is not
if time.Since(t) > 500*time.Millisecond { // done too frequently causing a stall of the peerDB operations.
pKey, pEntry, err := n.driver.peerDbSearch(n.id, ip)
if err == nil && !pEntry.isLocal {
t = time.Now() t = time.Now()
n.programNeighbor(ip) logrus.Warnf("miss notification for peer:%+v l3Miss:%t l2Miss:%t, if the problem persist check the gc_thresh on the host pKey:%+v pEntry:%+v err:%v",
neigh, l3Miss, l2Miss, *pKey, *pEntry, err)
} }
} }
} }
} }
} }
func (n *network) programNeighbor(ip net.IP) {
peerMac, _, _, err := n.driver.peerDbSearch(n.id, ip)
if err != nil {
logrus.Errorf("Reprogramming on L3 miss failed for %s, no peer entry", ip)
return
}
s := n.getSubnetforIPAddr(ip)
if s == nil {
logrus.Errorf("Reprogramming on L3 miss failed for %s, not a valid subnet", ip)
return
}
sbox := n.sandbox()
if sbox == nil {
logrus.Errorf("Reprogramming on L3 miss failed for %s, overlay sandbox missing", ip)
return
}
if err := sbox.AddNeighbor(ip, peerMac, true, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
logrus.Errorf("Reprogramming on L3 miss failed for %s: %v", ip, err)
return
}
}
func (d *driver) addNetwork(n *network) { func (d *driver) addNetwork(n *network) {
d.Lock() d.Lock()
d.networks[n.id] = n d.networks[n.id] = n
@ -1090,15 +1067,6 @@ func (n *network) contains(ip net.IP) bool {
return false return false
} }
func (n *network) getSubnetforIPAddr(ip net.IP) *subnet {
for _, s := range n.subnets {
if s.subnetIP.Contains(ip) {
return s
}
}
return nil
}
// getSubnetforIP returns the subnet to which the given IP belongs // getSubnetforIP returns the subnet to which the given IP belongs
func (n *network) getSubnetforIP(ip *net.IPNet) *subnet { func (n *network) getSubnetforIP(ip *net.IPNet) *subnet {
for _, s := range n.subnets { for _, s := range n.subnets {

View file

@ -122,7 +122,7 @@ func (d *driver) processEvent(u serf.UserEvent) {
case "join": case "join":
d.peerAdd(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac, net.ParseIP(vtepStr), false, false, false) d.peerAdd(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac, net.ParseIP(vtepStr), false, false, false)
case "leave": case "leave":
d.peerDelete(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac, net.ParseIP(vtepStr)) d.peerDelete(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac, net.ParseIP(vtepStr), false)
} }
} }
@ -135,13 +135,13 @@ func (d *driver) processQuery(q *serf.Query) {
fmt.Printf("Failed to scan query payload string: %v\n", err) fmt.Printf("Failed to scan query payload string: %v\n", err)
} }
peerMac, peerIPMask, vtep, err := d.peerDbSearch(nid, net.ParseIP(ipStr)) pKey, pEntry, err := d.peerDbSearch(nid, net.ParseIP(ipStr))
if err != nil { if err != nil {
return return
} }
logrus.Debugf("Sending peer query resp mac %s, mask %s, vtep %s", peerMac, net.IP(peerIPMask), vtep) logrus.Debugf("Sending peer query resp mac %v, mask %s, vtep %s", pKey.peerMac, net.IP(pEntry.peerIPMask).String(), pEntry.vtep)
q.Respond([]byte(fmt.Sprintf("%s %s %s", peerMac.String(), net.IP(peerIPMask).String(), vtep.String()))) q.Respond([]byte(fmt.Sprintf("%s %s %s", pKey.peerMac.String(), net.IP(pEntry.peerIPMask).String(), pEntry.vtep.String())))
} }
func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) { func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {

View file

@ -8,6 +8,7 @@ import (
"syscall" "syscall"
"github.com/docker/libnetwork/common" "github.com/docker/libnetwork/common"
"github.com/docker/libnetwork/osl"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -22,16 +23,45 @@ type peerEntry struct {
eid string eid string
vtep net.IP vtep net.IP
peerIPMask net.IPMask peerIPMask net.IPMask
inSandbox bool
isLocal bool isLocal bool
} }
func (p *peerEntry) MarshalDB() peerEntryDB {
return peerEntryDB{
eid: p.eid,
vtep: p.vtep.String(),
peerIPMask: p.peerIPMask.String(),
isLocal: p.isLocal,
}
}
// This the structure saved into the set (SetMatrix), due to the implementation of it
// the value inserted in the set has to be Hashable so the []byte had to be converted into
// strings
type peerEntryDB struct {
eid string
vtep string
peerIPMask string
isLocal bool
}
func (p *peerEntryDB) UnMarshalDB() peerEntry {
return peerEntry{
eid: p.eid,
vtep: net.ParseIP(p.vtep),
peerIPMask: net.IPMask(net.ParseIP(p.peerIPMask)),
isLocal: p.isLocal,
}
}
type peerMap struct { type peerMap struct {
mp map[string]peerEntry // set of peerEntry, note they have to be objects and not pointers to maintain the proper equality checks
mp common.SetMatrix
sync.Mutex sync.Mutex
} }
type peerNetworkMap struct { type peerNetworkMap struct {
// map with key peerKey
mp map[string]*peerMap mp map[string]*peerMap
sync.Mutex sync.Mutex
} }
@ -54,13 +84,9 @@ func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error {
} }
pKey.peerMac, err = net.ParseMAC(string(macB)) pKey.peerMac, err = net.ParseMAC(string(macB))
if err != nil {
return err return err
} }
return nil
}
func (d *driver) peerDbWalk(f func(string, *peerKey, *peerEntry) bool) error { func (d *driver) peerDbWalk(f func(string, *peerKey, *peerEntry) bool) error {
d.peerDb.Lock() d.peerDb.Lock()
nids := []string{} nids := []string{}
@ -87,10 +113,13 @@ func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool
} }
mp := map[string]peerEntry{} mp := map[string]peerEntry{}
pMap.Lock() pMap.Lock()
for pKeyStr, pEntry := range pMap.mp { for _, pKeyStr := range pMap.mp.Keys() {
mp[pKeyStr] = pEntry entryDBList, ok := pMap.mp.Get(pKeyStr)
if ok {
peerEntryDB := entryDBList[0].(peerEntryDB)
mp[pKeyStr] = peerEntryDB.UnMarshalDB()
}
} }
pMap.Unlock() pMap.Unlock()
@ -107,45 +136,38 @@ func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool
return nil return nil
} }
func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) { func (d *driver) peerDbSearch(nid string, peerIP net.IP) (*peerKey, *peerEntry, error) {
var ( var pKeyMatched *peerKey
peerMac net.HardwareAddr var pEntryMatched *peerEntry
vtep net.IP
peerIPMask net.IPMask
found bool
)
err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool { err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
if pKey.peerIP.Equal(peerIP) { if pKey.peerIP.Equal(peerIP) {
peerMac = pKey.peerMac pKeyMatched = pKey
peerIPMask = pEntry.peerIPMask pEntryMatched = pEntry
vtep = pEntry.vtep return true
found = true
return found
} }
return found return false
}) })
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err) return nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
} }
if !found { if pKeyMatched == nil || pEntryMatched == nil {
return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP) return nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
} }
return peerMac, peerIPMask, vtep, nil return pKeyMatched, pEntryMatched, nil
} }
func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
peerMac net.HardwareAddr, vtep net.IP, isLocal bool) { peerMac net.HardwareAddr, vtep net.IP, isLocal bool) (bool, int) {
d.peerDb.Lock() d.peerDb.Lock()
pMap, ok := d.peerDb.mp[nid] pMap, ok := d.peerDb.mp[nid]
if !ok { if !ok {
d.peerDb.mp[nid] = &peerMap{ d.peerDb.mp[nid] = &peerMap{
mp: make(map[string]peerEntry), mp: common.NewSetMatrix(),
} }
pMap = d.peerDb.mp[nid] pMap = d.peerDb.mp[nid]
@ -165,18 +187,24 @@ func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask
} }
pMap.Lock() pMap.Lock()
pMap.mp[pKey.String()] = pEntry defer pMap.Unlock()
pMap.Unlock() b, i := pMap.mp.Insert(pKey.String(), pEntry.MarshalDB())
if i != 1 {
// Transient case, there is more than one endpoint that is using the same IP,MAC pair
s, _ := pMap.mp.String(pKey.String())
logrus.Warnf("peerDbAdd transient condition - Key:%s cardinality:%d db state:%s", pKey.String(), i, s)
}
return b, i
} }
func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
peerMac net.HardwareAddr, vtep net.IP) peerEntry { peerMac net.HardwareAddr, vtep net.IP, isLocal bool) (bool, int) {
d.peerDb.Lock() d.peerDb.Lock()
pMap, ok := d.peerDb.mp[nid] pMap, ok := d.peerDb.mp[nid]
if !ok { if !ok {
d.peerDb.Unlock() d.peerDb.Unlock()
return peerEntry{} return false, 0
} }
d.peerDb.Unlock() d.peerDb.Unlock()
@ -185,22 +213,22 @@ func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPM
peerMac: peerMac, peerMac: peerMac,
} }
pEntry := peerEntry{
eid: eid,
vtep: vtep,
peerIPMask: peerIPMask,
isLocal: isLocal,
}
pMap.Lock() pMap.Lock()
defer pMap.Unlock()
pEntry, ok := pMap.mp[pKey.String()] b, i := pMap.mp.Remove(pKey.String(), pEntry.MarshalDB())
if ok { if i != 0 {
// Mismatched endpoint ID(possibly outdated). Do not // Transient case, there is more than one endpoint that is using the same IP,MAC pair
// delete peerdb s, _ := pMap.mp.String(pKey.String())
if pEntry.eid != eid { logrus.Warnf("peerDbDelete transient condition - Key:%s cardinality:%d db state:%s", pKey.String(), i, s)
pMap.Unlock()
return pEntry
} }
} return b, i
delete(pMap.mp, pKey.String())
pMap.Unlock()
return pEntry
} }
// The overlay uses a lazy initialization approach, this means that when a network is created // The overlay uses a lazy initialization approach, this means that when a network is created
@ -224,6 +252,7 @@ const (
peerOperationINIT peerOperationType = iota peerOperationINIT peerOperationType = iota
peerOperationADD peerOperationADD
peerOperationDELETE peerOperationDELETE
peerOperationFLUSH
) )
type peerOperation struct { type peerOperation struct {
@ -253,7 +282,9 @@ func (d *driver) peerOpRoutine(ctx context.Context, ch chan *peerOperation) {
case peerOperationADD: case peerOperationADD:
err = d.peerAddOp(op.networkID, op.endpointID, op.peerIP, op.peerIPMask, op.peerMac, op.vtepIP, op.l2Miss, op.l3Miss, true, op.localPeer) err = d.peerAddOp(op.networkID, op.endpointID, op.peerIP, op.peerIPMask, op.peerMac, op.vtepIP, op.l2Miss, op.l3Miss, true, op.localPeer)
case peerOperationDELETE: case peerOperationDELETE:
err = d.peerDeleteOp(op.networkID, op.endpointID, op.peerIP, op.peerIPMask, op.peerMac, op.vtepIP) err = d.peerDeleteOp(op.networkID, op.endpointID, op.peerIP, op.peerIPMask, op.peerMac, op.vtepIP, op.localPeer)
case peerOperationFLUSH:
err = d.peerFlushOp(op.networkID)
} }
if err != nil { if err != nil {
logrus.Warnf("Peer operation failed:%s op:%v", err, op) logrus.Warnf("Peer operation failed:%s op:%v", err, op)
@ -286,7 +317,6 @@ func (d *driver) peerInitOp(nid string) error {
func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
peerMac net.HardwareAddr, vtep net.IP, l2Miss, l3Miss, localPeer bool) { peerMac net.HardwareAddr, vtep net.IP, l2Miss, l3Miss, localPeer bool) {
callerName := common.CallerName(1)
d.peerOpCh <- &peerOperation{ d.peerOpCh <- &peerOperation{
opType: peerOperationADD, opType: peerOperationADD,
networkID: nid, networkID: nid,
@ -298,24 +328,32 @@ func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
l2Miss: l2Miss, l2Miss: l2Miss,
l3Miss: l3Miss, l3Miss: l3Miss,
localPeer: localPeer, localPeer: localPeer,
callerName: callerName, callerName: common.CallerName(1),
} }
} }
func (d *driver) peerAddOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, func (d *driver) peerAddOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
peerMac net.HardwareAddr, vtep net.IP, l2Miss, l3Miss, updateDB, updateOnlyDB bool) error { peerMac net.HardwareAddr, vtep net.IP, l2Miss, l3Miss, updateDB, localPeer bool) error {
if err := validateID(nid, eid); err != nil { if err := validateID(nid, eid); err != nil {
return err return err
} }
var dbEntries int
var inserted bool
if updateDB { if updateDB {
d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false) inserted, dbEntries = d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, localPeer)
if updateOnlyDB { if !inserted {
return nil logrus.Warnf("Entry already present in db: nid:%s eid:%s peerIP:%v peerMac:%v isLocal:%t vtep:%v",
nid, eid, peerIP, peerMac, localPeer, vtep)
} }
} }
// Local peers do not need any further configuration
if localPeer {
return nil
}
n := d.network(nid) n := d.network(nid)
if n == nil { if n == nil {
return nil return nil
@ -353,21 +391,26 @@ func (d *driver) peerAddOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask
// Add neighbor entry for the peer IP // Add neighbor entry for the peer IP
if err := sbox.AddNeighbor(peerIP, peerMac, l3Miss, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil { if err := sbox.AddNeighbor(peerIP, peerMac, l3Miss, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
return fmt.Errorf("could not add neighbor entry into the sandbox: %v", err) if _, ok := err.(osl.NeighborSearchError); ok && dbEntries > 1 {
// We are in the transient case so only the first configuration is programmed into the kernel
// Upon deletion if the active configuration is deleted the next one from the database will be restored
// Note we are skipping also the next configuration
return nil
}
return fmt.Errorf("could not add neighbor entry for nid:%s eid:%s into the sandbox:%v", nid, eid, err)
} }
// Add fdb entry to the bridge for the peer mac // Add fdb entry to the bridge for the peer mac
if err := sbox.AddNeighbor(vtep, peerMac, l2Miss, sbox.NeighborOptions().LinkName(s.vxlanName), if err := sbox.AddNeighbor(vtep, peerMac, l2Miss, sbox.NeighborOptions().LinkName(s.vxlanName),
sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil { sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil {
return fmt.Errorf("could not add fdb entry into the sandbox: %v", err) return fmt.Errorf("could not add fdb entry for nid:%s eid:%s into the sandbox:%v", nid, eid, err)
} }
return nil return nil
} }
func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
peerMac net.HardwareAddr, vtep net.IP) { peerMac net.HardwareAddr, vtep net.IP, localPeer bool) {
callerName := common.CallerName(1)
d.peerOpCh <- &peerOperation{ d.peerOpCh <- &peerOperation{
opType: peerOperationDELETE, opType: peerOperationDELETE,
networkID: nid, networkID: nid,
@ -376,18 +419,23 @@ func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMas
peerIPMask: peerIPMask, peerIPMask: peerIPMask,
peerMac: peerMac, peerMac: peerMac,
vtepIP: vtep, vtepIP: vtep,
callerName: callerName, callerName: common.CallerName(1),
localPeer: localPeer,
} }
} }
func (d *driver) peerDeleteOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, func (d *driver) peerDeleteOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
peerMac net.HardwareAddr, vtep net.IP) error { peerMac net.HardwareAddr, vtep net.IP, localPeer bool) error {
if err := validateID(nid, eid); err != nil { if err := validateID(nid, eid); err != nil {
return err return err
} }
pEntry := d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep) deleted, dbEntries := d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep, localPeer)
if !deleted {
logrus.Warnf("Entry was not in db: nid:%s eid:%s peerIP:%v peerMac:%v isLocal:%t vtep:%v",
nid, eid, peerIP, peerMac, localPeer, vtep)
}
n := d.network(nid) n := d.network(nid)
if n == nil { if n == nil {
@ -399,30 +447,59 @@ func (d *driver) peerDeleteOp(nid, eid string, peerIP net.IP, peerIPMask net.IPM
return nil return nil
} }
// Delete fdb entry to the bridge for the peer mac only if the if err := d.checkEncryption(nid, vtep, 0, localPeer, false); err != nil {
// entry existed in local peerdb. If it is a stale delete
// request, still call DeleteNeighbor but only to cleanup any
// leftover sandbox neighbor cache and not actually delete the
// kernel state.
if (eid == pEntry.eid && vtep.Equal(pEntry.vtep)) ||
(eid != pEntry.eid && !vtep.Equal(pEntry.vtep)) {
if err := sbox.DeleteNeighbor(vtep, peerMac,
eid == pEntry.eid && vtep.Equal(pEntry.vtep)); err != nil {
return fmt.Errorf("could not delete fdb entry into the sandbox: %v", err)
}
}
// Delete neighbor entry for the peer IP
if eid == pEntry.eid {
if err := sbox.DeleteNeighbor(peerIP, peerMac, true); err != nil {
return fmt.Errorf("could not delete neighbor entry into the sandbox: %v", err)
}
}
if err := d.checkEncryption(nid, vtep, 0, false, false); err != nil {
logrus.Warn(err) logrus.Warn(err)
} }
// Local peers do not have any local configuration to delete
if !localPeer {
// Remove fdb entry to the bridge for the peer mac
if err := sbox.DeleteNeighbor(vtep, peerMac, true); err != nil {
if _, ok := err.(osl.NeighborSearchError); ok && dbEntries > 0 {
// We fall in here if there is a transient state and if the neighbor that is being deleted
// was never been configured into the kernel (we allow only 1 configuration at the time per <ip,mac> mapping)
return nil
}
return fmt.Errorf("could not delete fdb entry for nid:%s eid:%s into the sandbox:%v", nid, eid, err)
}
// Delete neighbor entry for the peer IP
if err := sbox.DeleteNeighbor(peerIP, peerMac, true); err != nil {
return fmt.Errorf("could not delete neighbor entry for nid:%s eid:%s into the sandbox:%v", nid, eid, err)
}
}
if dbEntries == 0 {
return nil
}
// If there is still an entry into the database and the deletion went through without errors means that there is now no
// configuration active in the kernel.
// Restore one configuration for the <ip,mac> directly from the database, note that is guaranteed that there is one
peerKey, peerEntry, err := d.peerDbSearch(nid, peerIP)
if err != nil {
logrus.Errorf("peerDeleteOp unable to restore a configuration for nid:%s ip:%v mac:%v err:%s", nid, peerIP, peerMac, err)
return err
}
return d.peerAddOp(nid, peerEntry.eid, peerIP, peerEntry.peerIPMask, peerKey.peerMac, peerEntry.vtep, false, false, false, peerEntry.isLocal)
}
func (d *driver) peerFlush(nid string) {
d.peerOpCh <- &peerOperation{
opType: peerOperationFLUSH,
networkID: nid,
callerName: common.CallerName(1),
}
}
func (d *driver) peerFlushOp(nid string) error {
d.peerDb.Lock()
defer d.peerDb.Unlock()
_, ok := d.peerDb.mp[nid]
if !ok {
return fmt.Errorf("Unable to find the peerDB for nid:%s", nid)
}
delete(d.peerDb.mp, nid)
return nil return nil
} }

View file

@ -505,7 +505,10 @@ func (nDB *NetworkDB) deleteNodeNetworkEntries(nid, node string) {
nDB.deleteEntry(nid, tname, key) nDB.deleteEntry(nid, tname, key)
} }
// Notify to the upper layer only entries not already marked for deletion
if !oldEntry.deleting {
nDB.broadcaster.Write(makeEvent(opDelete, tname, nid, key, entry.value)) nDB.broadcaster.Write(makeEvent(opDelete, tname, nid, key, entry.value))
}
return false return false
}) })
} }

View file

@ -9,6 +9,17 @@ import (
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
// NeighborSearchError indicates that the neighbor is already present
type NeighborSearchError struct {
ip net.IP
mac net.HardwareAddr
present bool
}
func (n NeighborSearchError) Error() string {
return fmt.Sprintf("Search neighbor failed for IP %v, mac %v, present in db:%t", n.ip, n.mac, n.present)
}
// NeighOption is a function option type to set interface options // NeighOption is a function option type to set interface options
type NeighOption func(nh *neigh) type NeighOption func(nh *neigh)
@ -41,7 +52,7 @@ func (n *networkNamespace) DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr,
nh := n.findNeighbor(dstIP, dstMac) nh := n.findNeighbor(dstIP, dstMac)
if nh == nil { if nh == nil {
return fmt.Errorf("could not find the neighbor entry to delete") return NeighborSearchError{dstIP, dstMac, false}
} }
if osDelete { if osDelete {
@ -103,7 +114,7 @@ func (n *networkNamespace) DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr,
} }
} }
n.Unlock() n.Unlock()
logrus.Debugf("Neighbor entry deleted for IP %v, mac %v", dstIP, dstMac) logrus.Debugf("Neighbor entry deleted for IP %v, mac %v osDelete:%t", dstIP, dstMac, osDelete)
return nil return nil
} }
@ -112,17 +123,18 @@ func (n *networkNamespace) AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, fo
var ( var (
iface netlink.Link iface netlink.Link
err error err error
neighborAlreadyPresent bool
) )
// If the namespace already has the neighbor entry but the AddNeighbor is called // If the namespace already has the neighbor entry but the AddNeighbor is called
// because of a miss notification (force flag) program the kernel anyway. // because of a miss notification (force flag) program the kernel anyway.
nh := n.findNeighbor(dstIP, dstMac) nh := n.findNeighbor(dstIP, dstMac)
if nh != nil { if nh != nil {
neighborAlreadyPresent = true
logrus.Warnf("Neighbor entry already present for IP %v, mac %v neighbor:%+v forceUpdate:%t", dstIP, dstMac, nh, force)
if !force { if !force {
logrus.Warnf("Neighbor entry already present for IP %v, mac %v", dstIP, dstMac) return NeighborSearchError{dstIP, dstMac, true}
return nil
} }
logrus.Warnf("Force kernel update, Neighbor entry already present for IP %v, mac %v", dstIP, dstMac)
} }
nh = &neigh{ nh = &neigh{
@ -146,8 +158,7 @@ func (n *networkNamespace) AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, fo
if nh.linkDst != "" { if nh.linkDst != "" {
iface, err = nlh.LinkByName(nh.linkDst) iface, err = nlh.LinkByName(nh.linkDst)
if err != nil { if err != nil {
return fmt.Errorf("could not find interface with destination name %s: %v", return fmt.Errorf("could not find interface with destination name %s: %v", nh.linkDst, err)
nh.linkDst, err)
} }
} }
@ -167,13 +178,17 @@ func (n *networkNamespace) AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, fo
} }
if err := nlh.NeighSet(nlnh); err != nil { if err := nlh.NeighSet(nlnh); err != nil {
return fmt.Errorf("could not add neighbor entry: %v", err) return fmt.Errorf("could not add neighbor entry:%+v error:%v", nlnh, err)
}
if neighborAlreadyPresent {
return nil
} }
n.Lock() n.Lock()
n.neighbors = append(n.neighbors, nh) n.neighbors = append(n.neighbors, nh)
n.Unlock() n.Unlock()
logrus.Debugf("Neighbor entry added for IP %v, mac %v", dstIP, dstMac) logrus.Debugf("Neighbor entry added for IP:%v, mac:%v on ifc:%s", dstIP, dstMac, nh.linkName)
return nil return nil
} }