mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
524b3dca97
When you start a container after some other container has already been started in the same network, the current container will have an fdb which points to a wrong vtep to reach the already started container. This makes the network connectivity to not work. The root cause of the issue is because of golang does variable capture by reference in closures and so we cannot use the return values from range iterators directly. It needs to be copied to a locally scoped variable and then use that copy as a capture variable in the closure. Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
284 lines
5.3 KiB
Go
284 lines
5.3 KiB
Go
package overlay
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"syscall"
|
|
|
|
"github.com/docker/libnetwork/types"
|
|
)
|
|
|
|
type peerKey struct {
|
|
peerIP net.IP
|
|
peerMac net.HardwareAddr
|
|
}
|
|
|
|
type peerEntry struct {
|
|
eid types.UUID
|
|
vtep net.IP
|
|
inSandbox bool
|
|
isLocal bool
|
|
}
|
|
|
|
type peerMap struct {
|
|
mp map[string]peerEntry
|
|
sync.Mutex
|
|
}
|
|
|
|
type peerNetworkMap struct {
|
|
mp map[types.UUID]peerMap
|
|
sync.Mutex
|
|
}
|
|
|
|
func (pKey peerKey) String() string {
|
|
return fmt.Sprintf("%s %s", pKey.peerIP, pKey.peerMac)
|
|
}
|
|
|
|
func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error {
|
|
ipB, err := state.Token(true, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pKey.peerIP = net.ParseIP(string(ipB))
|
|
|
|
macB, err := state.Token(true, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pKey.peerMac, err = net.ParseMAC(string(macB))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var peerDbWg sync.WaitGroup
|
|
|
|
func (d *driver) peerDbWalk(nid types.UUID, f func(*peerKey, *peerEntry) bool) error {
|
|
d.peerDb.Lock()
|
|
pMap, ok := d.peerDb.mp[nid]
|
|
if !ok {
|
|
d.peerDb.Unlock()
|
|
return nil
|
|
}
|
|
d.peerDb.Unlock()
|
|
|
|
pMap.Lock()
|
|
for pKeyStr, pEntry := range pMap.mp {
|
|
var pKey peerKey
|
|
if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
|
|
fmt.Printf("peer key scan failed: %v", err)
|
|
}
|
|
|
|
if f(&pKey, &pEntry) {
|
|
pMap.Unlock()
|
|
return nil
|
|
}
|
|
}
|
|
pMap.Unlock()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) peerDbSearch(nid types.UUID, peerIP net.IP) (net.HardwareAddr, net.IP, error) {
|
|
var (
|
|
peerMac net.HardwareAddr
|
|
vtep net.IP
|
|
found bool
|
|
)
|
|
|
|
err := d.peerDbWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
|
|
if pKey.peerIP.Equal(peerIP) {
|
|
peerMac = pKey.peerMac
|
|
vtep = pEntry.vtep
|
|
found = true
|
|
return found
|
|
}
|
|
|
|
return found
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
|
|
}
|
|
|
|
if !found {
|
|
return nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
|
|
}
|
|
|
|
return peerMac, vtep, nil
|
|
}
|
|
|
|
func (d *driver) peerDbAdd(nid, eid types.UUID, peerIP net.IP,
|
|
peerMac net.HardwareAddr, vtep net.IP, isLocal bool) {
|
|
|
|
peerDbWg.Wait()
|
|
|
|
d.peerDb.Lock()
|
|
pMap, ok := d.peerDb.mp[nid]
|
|
if !ok {
|
|
d.peerDb.mp[nid] = peerMap{
|
|
mp: make(map[string]peerEntry),
|
|
}
|
|
|
|
pMap = d.peerDb.mp[nid]
|
|
}
|
|
d.peerDb.Unlock()
|
|
|
|
pKey := peerKey{
|
|
peerIP: peerIP,
|
|
peerMac: peerMac,
|
|
}
|
|
|
|
pEntry := peerEntry{
|
|
eid: eid,
|
|
vtep: vtep,
|
|
isLocal: isLocal,
|
|
}
|
|
|
|
pMap.Lock()
|
|
pMap.mp[pKey.String()] = pEntry
|
|
pMap.Unlock()
|
|
}
|
|
|
|
func (d *driver) peerDbDelete(nid, eid types.UUID, peerIP net.IP,
|
|
peerMac net.HardwareAddr, vtep net.IP) {
|
|
peerDbWg.Wait()
|
|
|
|
d.peerDb.Lock()
|
|
pMap, ok := d.peerDb.mp[nid]
|
|
if !ok {
|
|
d.peerDb.Unlock()
|
|
return
|
|
}
|
|
d.peerDb.Unlock()
|
|
|
|
pKey := peerKey{
|
|
peerIP: peerIP,
|
|
peerMac: peerMac,
|
|
}
|
|
|
|
pMap.Lock()
|
|
delete(pMap.mp, pKey.String())
|
|
pMap.Unlock()
|
|
}
|
|
|
|
func (d *driver) peerDbUpdateSandbox(nid types.UUID) {
|
|
d.peerDb.Lock()
|
|
pMap, ok := d.peerDb.mp[nid]
|
|
if !ok {
|
|
d.peerDb.Unlock()
|
|
return
|
|
}
|
|
d.peerDb.Unlock()
|
|
|
|
peerDbWg.Add(1)
|
|
|
|
var peerOps []func()
|
|
pMap.Lock()
|
|
for pKeyStr, pEntry := range pMap.mp {
|
|
var pKey peerKey
|
|
if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
|
|
fmt.Printf("peer key scan failed: %v", err)
|
|
}
|
|
|
|
if pEntry.isLocal {
|
|
continue
|
|
}
|
|
|
|
// Go captures variables by reference. The pEntry could be
|
|
// pointing to the same memory location for every iteration. Make
|
|
// a copy of pEntry before capturing it in the following closure.
|
|
entry := pEntry
|
|
op := func() {
|
|
if err := d.peerAdd(nid, entry.eid, pKey.peerIP,
|
|
pKey.peerMac, entry.vtep,
|
|
false); err != nil {
|
|
fmt.Printf("peerdbupdate in sandbox failed for ip %s and mac %s: %v",
|
|
pKey.peerIP, pKey.peerMac, err)
|
|
}
|
|
}
|
|
|
|
peerOps = append(peerOps, op)
|
|
}
|
|
pMap.Unlock()
|
|
|
|
for _, op := range peerOps {
|
|
op()
|
|
}
|
|
|
|
peerDbWg.Done()
|
|
}
|
|
|
|
func (d *driver) peerAdd(nid, eid types.UUID, peerIP net.IP,
|
|
peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {
|
|
|
|
if err := validateID(nid, eid); err != nil {
|
|
return err
|
|
}
|
|
|
|
if updateDb {
|
|
d.peerDbAdd(nid, eid, peerIP, peerMac, vtep, false)
|
|
}
|
|
|
|
n := d.network(nid)
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
|
|
sbox := n.sandbox()
|
|
if sbox == nil {
|
|
return nil
|
|
}
|
|
|
|
// Add neighbor entry for the peer IP
|
|
if err := sbox.AddNeighbor(peerIP, peerMac, sbox.NeighborOptions().LinkName(n.vxlanName)); err != nil {
|
|
return fmt.Errorf("could not add neigbor entry into the sandbox: %v", err)
|
|
}
|
|
|
|
// Add fdb entry to the bridge for the peer mac
|
|
if err := sbox.AddNeighbor(vtep, peerMac, sbox.NeighborOptions().LinkName(n.vxlanName),
|
|
sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil {
|
|
return fmt.Errorf("could not add fdb entry into the sandbox: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) peerDelete(nid, eid types.UUID, peerIP net.IP,
|
|
peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {
|
|
|
|
if err := validateID(nid, eid); err != nil {
|
|
return err
|
|
}
|
|
|
|
if updateDb {
|
|
d.peerDbDelete(nid, eid, peerIP, peerMac, vtep)
|
|
}
|
|
|
|
n := d.network(nid)
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
|
|
sbox := n.sandbox()
|
|
if sbox == nil {
|
|
return nil
|
|
}
|
|
|
|
// Delete fdb entry to the bridge for the peer mac
|
|
if err := sbox.DeleteNeighbor(vtep, peerMac); err != nil {
|
|
return fmt.Errorf("could not delete fdb entry into the sandbox: %v", err)
|
|
}
|
|
|
|
// Delete neighbor entry for the peer IP
|
|
if err := sbox.DeleteNeighbor(peerIP, peerMac); err != nil {
|
|
return fmt.Errorf("could not delete neigbor entry into the sandbox: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|