libn/i/setmatrix: make generic and constructorless
Related to https://github.com/moby/moby/pull/45163#discussion_r1137207454

Allow SetMatrix to be used as a value type with a ready-to-use zero value. SetMatrix values are already non-copyable by virtue of having a mutex field so there is no harm in allowing non-pointer values to be used as local variables or struct fields. Any attempts to pass around by-value copies, e.g. as function arguments, will be flagged by go vet.

<!--
Please make sure you've read and understood our contributing guidelines;
https://github.com/moby/moby/blob/master/CONTRIBUTING.md

** Make sure all your commits include a signature generated with `git commit -s` **

For additional information on our contributing process, read our contributing
guide https://docs.docker.com/opensource/code/

If this is a bug fix, make sure your description includes "fixes #xxxx", or
"closes #xxxx"

Please provide the following information:
-->

**- What I did**

**- How I did it**

**- How to verify it**

**- Description for the changelog**
<!--
Write a short (one line) summary that describes the changes in this
pull request for inclusion in the changelog:
-->


**- A picture of a cute animal (not mandatory but encouraged)**


libnetwork/network.go
package libnetwork

import (
	"encoding/json"
	"fmt"
	"net"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/docker/docker/libnetwork/config"
	"github.com/docker/docker/libnetwork/datastore"
	"github.com/docker/docker/libnetwork/driverapi"
	"github.com/docker/docker/libnetwork/etchosts"
	"github.com/docker/docker/libnetwork/internal/setmatrix"
	"github.com/docker/docker/libnetwork/ipamapi"
	"github.com/docker/docker/libnetwork/netlabel"
	"github.com/docker/docker/libnetwork/netutils"
	"github.com/docker/docker/libnetwork/networkdb"
	"github.com/docker/docker/libnetwork/options"
	"github.com/docker/docker/libnetwork/types"
	"github.com/docker/docker/pkg/stringid"
	"github.com/sirupsen/logrus"
)

// A Network represents a logical connectivity zone that containers may
// join using the Link method. A Network is managed by a specific driver.
type Network interface {
	// Name returns a user chosen name for this network.
	Name() string

	// ID returns a system generated id for this network.
	ID() string

	// Type returns the type of network, which corresponds to its managing driver.
	Type() string

	// CreateEndpoint creates a new endpoint to this network symbolically identified by the
	// specified unique name. The options parameter carries driver specific options.
	CreateEndpoint(name string, options ...EndpointOption) (*Endpoint, error)

	// Delete the network.
	Delete(options ...NetworkDeleteOption) error

	// Endpoints returns the list of Endpoint(s) in this network.
	Endpoints() []*Endpoint

	// WalkEndpoints uses the provided function to walk the Endpoints.
	WalkEndpoints(walker EndpointWalker)

	// EndpointByName returns the Endpoint which has the passed name. If not found, the error ErrNoSuchEndpoint is returned.
	EndpointByName(name string) (*Endpoint, error)

	// EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned.
	EndpointByID(id string) (*Endpoint, error)

	// Info returns certain operational data belonging to this network.
	Info() NetworkInfo
}

// NetworkInfo returns some configuration and operational information about the network
type NetworkInfo interface {
	IpamConfig() (string, map[string]string, []*IpamConf, []*IpamConf)
	IpamInfo() ([]*IpamInfo, []*IpamInfo)
	DriverOptions() map[string]string
	Scope() string
	IPv6Enabled() bool
	Internal() bool
	Attachable() bool
	Ingress() bool
	ConfigFrom() string
	ConfigOnly() bool
	Labels() map[string]string
	Dynamic() bool
	Created() time.Time
	// Peers returns a slice of PeerInfo structures which has the information about the peer
	// nodes participating in the same overlay network. This is currently the per-network
	// gossip cluster. For non-dynamic overlay networks and bridge networks it returns an
	// empty slice
	Peers() []networkdb.PeerInfo
	// Services returns a map of services keyed by the service name with the details
	// of all the tasks that belong to the service. Applicable only in swarm mode.
	Services() map[string]ServiceInfo
}

// EndpointWalker is a client provided function which will be used to walk the Endpoints.
// When the function returns true, the walk will stop.
type EndpointWalker func(ep *Endpoint) bool

// ipInfo is the reverse mapping from IP to service name to serve the PTR query.
// extResolver is set if an external server resolves a service name to this IP.
// It's an indication to defer PTR queries also to that external server.
type ipInfo struct {
	name        string
	serviceID   string
	extResolver bool
}

// svcMapEntry is the body of the element into the svcMap
// The ip is a string because the SetMatrix does not accept non hashable values
type svcMapEntry struct {
	ip        string
	serviceID string
}

type svcInfo struct {
	svcMap     *setmatrix.SetMatrix
	svcIPv6Map *setmatrix.SetMatrix
	ipMap      *setmatrix.SetMatrix
	service    map[string][]servicePorts
}

// backing container or host's info
type serviceTarget struct {
	name string
	ip   net.IP
	port uint16
}

type servicePorts struct {
	portName string
	proto    string
	target   []serviceTarget
}

type networkDBTable struct {
	name    string
	objType driverapi.ObjectType
}

// IpamConf contains all the ipam related configurations for a network
type IpamConf struct {
	// PreferredPool is the master address pool for containers and network interfaces.
	PreferredPool string
	// SubPool is a subset of the master pool. If specified,
	// this becomes the container pool.
	SubPool string
	// Gateway is the preferred Network Gateway address (optional).
	Gateway string
	// AuxAddresses contains auxiliary addresses for network driver. Must be within the master pool.
	// libnetwork will reserve them if they fall into the container pool.
	AuxAddresses map[string]string
}

// Validate checks whether the configuration is valid
func (c *IpamConf) Validate() error {
	if c.Gateway != "" && nil == net.ParseIP(c.Gateway) {
		return types.BadRequestErrorf("invalid gateway address %s in Ipam configuration", c.Gateway)
	}
	return nil
}

// IpamInfo contains all the ipam related operational info for a network
type IpamInfo struct {
	PoolID string
	Meta   map[string]string
	driverapi.IPAMData
}

// MarshalJSON encodes IpamInfo into json message
func (i *IpamInfo) MarshalJSON() ([]byte, error) {
	m := map[string]interface{}{
		"PoolID": i.PoolID,
	}
	v, err := json.Marshal(&i.IPAMData)
	if err != nil {
		return nil, err
	}
	m["IPAMData"] = string(v)

	if i.Meta != nil {
		m["Meta"] = i.Meta
	}
	return json.Marshal(m)
}

// UnmarshalJSON decodes json message into PoolData
func (i *IpamInfo) UnmarshalJSON(data []byte) error {
	var (
		m   map[string]interface{}
		err error
	)
	if err = json.Unmarshal(data, &m); err != nil {
		return err
	}
	i.PoolID = m["PoolID"].(string)
	if v, ok := m["Meta"]; ok {
		b, _ := json.Marshal(v)
		if err = json.Unmarshal(b, &i.Meta); err != nil {
			return err
		}
	}
	if v, ok := m["IPAMData"]; ok {
		if err = json.Unmarshal([]byte(v.(string)), &i.IPAMData); err != nil {
			return err
		}
	}
	return nil
}

type network struct {
	ctrlr            *Controller
	name             string
	networkType      string
	id               string
	created          time.Time
	scope            string // network data scope
	labels           map[string]string
	ipamType         string
	ipamOptions      map[string]string
	addrSpace        string
	ipamV4Config     []*IpamConf
	ipamV6Config     []*IpamConf
	ipamV4Info       []*IpamInfo
	ipamV6Info       []*IpamInfo
	enableIPv6       bool
	postIPv6         bool
	epCnt            *endpointCnt
	generic          options.Generic
	dbIndex          uint64
	dbExists         bool
	persist          bool
	drvOnce          *sync.Once
	resolverOnce     sync.Once //nolint:nolintlint,unused // only used on windows
	resolver         []*Resolver
	internal         bool
	attachable       bool
	inDelete         bool
	ingress          bool
	driverTables     []networkDBTable
	dynamic          bool
	configOnly       bool
	configFrom       string
	loadBalancerIP   net.IP
	loadBalancerMode string
	mu               sync.Mutex
}

const (
	loadBalancerModeNAT     = "NAT"
	loadBalancerModeDSR     = "DSR"
	loadBalancerModeDefault = loadBalancerModeNAT
)

func (n *network) Name() string {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.name
}

func (n *network) ID() string {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.id
}

func (n *network) Created() time.Time {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.created
}

func (n *network) Type() string {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.networkType
}

func (n *network) Key() []string {
	n.mu.Lock()
	defer n.mu.Unlock()
	return []string{datastore.NetworkKeyPrefix, n.id}
}

func (n *network) KeyPrefix() []string {
	return []string{datastore.NetworkKeyPrefix}
}

func (n *network) Value() []byte {
	n.mu.Lock()
	defer n.mu.Unlock()
	b, err := json.Marshal(n)
	if err != nil {
		return nil
	}
	return b
}

func (n *network) SetValue(value []byte) error {
	return json.Unmarshal(value, n)
}

func (n *network) Index() uint64 {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.dbIndex
}

func (n *network) SetIndex(index uint64) {
	n.mu.Lock()
	n.dbIndex = index
	n.dbExists = true
	n.mu.Unlock()
}

func (n *network) Exists() bool {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.dbExists
}

func (n *network) Skip() bool {
	n.mu.Lock()
	defer n.mu.Unlock()
	return !n.persist
}

func (n *network) New() datastore.KVObject {
	n.mu.Lock()
	defer n.mu.Unlock()

	return &network{
		ctrlr:   n.ctrlr,
		drvOnce: &sync.Once{},
		scope:   n.scope,
	}
}

// CopyTo deep copies to the destination IpamConfig
func (c *IpamConf) CopyTo(dstC *IpamConf) error {
	dstC.PreferredPool = c.PreferredPool
	dstC.SubPool = c.SubPool
	dstC.Gateway = c.Gateway
	if c.AuxAddresses != nil {
		dstC.AuxAddresses = make(map[string]string, len(c.AuxAddresses))
		for k, v := range c.AuxAddresses {
			dstC.AuxAddresses[k] = v
		}
	}
	return nil
}

// CopyTo deep copies to the destination IpamInfo
func (i *IpamInfo) CopyTo(dstI *IpamInfo) error {
	dstI.PoolID = i.PoolID
	if i.Meta != nil {
		dstI.Meta = make(map[string]string)
		for k, v := range i.Meta {
			dstI.Meta[k] = v
		}
	}

	dstI.AddressSpace = i.AddressSpace
	dstI.Pool = types.GetIPNetCopy(i.Pool)
	dstI.Gateway = types.GetIPNetCopy(i.Gateway)

	if i.AuxAddresses != nil {
		dstI.AuxAddresses = make(map[string]*net.IPNet)
		for k, v := range i.AuxAddresses {
			dstI.AuxAddresses[k] = types.GetIPNetCopy(v)
		}
	}

	return nil
}

func (n *network) validateConfiguration() error {
	if n.configOnly {
		// Only supports network specific configurations.
		// Network operator configurations are not supported.
		if n.ingress || n.internal || n.attachable || n.scope != "" {
			return types.ForbiddenErrorf("configuration network can only contain network " +
				"specific fields. Network operator fields like " +
				"[ ingress | internal | attachable | scope ] are not supported.")
		}
	}
	if n.configFrom != "" {
		if n.configOnly {
			return types.ForbiddenErrorf("a configuration network cannot depend on another configuration network")
		}
		if n.ipamType != "" &&
			n.ipamType != defaultIpamForNetworkType(n.networkType) ||
			n.enableIPv6 ||
			len(n.labels) > 0 || len(n.ipamOptions) > 0 ||
			len(n.ipamV4Config) > 0 || len(n.ipamV6Config) > 0 {
			return types.ForbiddenErrorf("user specified configurations are not supported if the network depends on a configuration network")
		}
		if len(n.generic) > 0 {
			if data, ok := n.generic[netlabel.GenericData]; ok {
				var (
					driverOptions map[string]string
					opts          interface{}
				)
				switch t := data.(type) {
				case map[string]interface{}, map[string]string:
					opts = t
				}
				ba, err := json.Marshal(opts)
				if err != nil {
					return fmt.Errorf("failed to validate network configuration: %v", err)
				}
				if err := json.Unmarshal(ba, &driverOptions); err != nil {
					return fmt.Errorf("failed to validate network configuration: %v", err)
				}
				if len(driverOptions) > 0 {
					return types.ForbiddenErrorf("network driver options are not supported if the network depends on a configuration network")
				}
			}
		}
	}
	return nil
}

// applyConfigurationTo applies network specific configurations.
func (n *network) applyConfigurationTo(to *network) error {
	to.enableIPv6 = n.enableIPv6
	if len(n.labels) > 0 {
		to.labels = make(map[string]string, len(n.labels))
		for k, v := range n.labels {
			if _, ok := to.labels[k]; !ok {
				to.labels[k] = v
			}
		}
	}
	if len(n.ipamType) != 0 {
		to.ipamType = n.ipamType
	}
	if len(n.ipamOptions) > 0 {
		to.ipamOptions = make(map[string]string, len(n.ipamOptions))
		for k, v := range n.ipamOptions {
			if _, ok := to.ipamOptions[k]; !ok {
				to.ipamOptions[k] = v
			}
		}
	}
	if len(n.ipamV4Config) > 0 {
		to.ipamV4Config = make([]*IpamConf, 0, len(n.ipamV4Config))
		to.ipamV4Config = append(to.ipamV4Config, n.ipamV4Config...)
	}
	if len(n.ipamV6Config) > 0 {
		to.ipamV6Config = make([]*IpamConf, 0, len(n.ipamV6Config))
		to.ipamV6Config = append(to.ipamV6Config, n.ipamV6Config...)
	}
	if len(n.generic) > 0 {
		to.generic = options.Generic{}
		for k, v := range n.generic {
			to.generic[k] = v
		}
	}
	return nil
}

func (n *network) CopyTo(o datastore.KVObject) error {
	n.mu.Lock()
	defer n.mu.Unlock()

	dstN := o.(*network)
	dstN.name = n.name
	dstN.id = n.id
	dstN.created = n.created
	dstN.networkType = n.networkType
	dstN.scope = n.scope
	dstN.dynamic = n.dynamic
	dstN.ipamType = n.ipamType
	dstN.enableIPv6 = n.enableIPv6
	dstN.persist = n.persist
	dstN.postIPv6 = n.postIPv6
	dstN.dbIndex = n.dbIndex
	dstN.dbExists = n.dbExists
	dstN.drvOnce = n.drvOnce
	dstN.internal = n.internal
	dstN.attachable = n.attachable
	dstN.inDelete = n.inDelete
	dstN.ingress = n.ingress
	dstN.configOnly = n.configOnly
	dstN.configFrom = n.configFrom
	dstN.loadBalancerIP = n.loadBalancerIP
	dstN.loadBalancerMode = n.loadBalancerMode

	// copy labels
	if dstN.labels == nil {
		dstN.labels = make(map[string]string, len(n.labels))
	}
	for k, v := range n.labels {
		dstN.labels[k] = v
	}

	if n.ipamOptions != nil {
		dstN.ipamOptions = make(map[string]string, len(n.ipamOptions))
		for k, v := range n.ipamOptions {
			dstN.ipamOptions[k] = v
		}
	}

	for _, v4conf := range n.ipamV4Config {
		dstV4Conf := &IpamConf{}
		if err := v4conf.CopyTo(dstV4Conf); err != nil {
			return err
		}
		dstN.ipamV4Config = append(dstN.ipamV4Config, dstV4Conf)
	}

	for _, v4info := range n.ipamV4Info {
		dstV4Info := &IpamInfo{}
		if err := v4info.CopyTo(dstV4Info); err != nil {
			return err
		}
		dstN.ipamV4Info = append(dstN.ipamV4Info, dstV4Info)
	}

	for _, v6conf := range n.ipamV6Config {
		dstV6Conf := &IpamConf{}
		if err := v6conf.CopyTo(dstV6Conf); err != nil {
			return err
		}
		dstN.ipamV6Config = append(dstN.ipamV6Config, dstV6Conf)
	}

	for _, v6info := range n.ipamV6Info {
		dstV6Info := &IpamInfo{}
		if err := v6info.CopyTo(dstV6Info); err != nil {
			return err
		}
		dstN.ipamV6Info = append(dstN.ipamV6Info, dstV6Info)
	}

	dstN.generic = options.Generic{}
	for k, v := range n.generic {
		dstN.generic[k] = v
	}

	return nil
}

func (n *network) DataScope() string {
	s := n.Scope()
	// All swarm scope networks have local datascope
	if s == datastore.SwarmScope {
		s = datastore.LocalScope
	}
	return s
}

func (n *network) getEpCnt() *endpointCnt {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.epCnt
}

// TODO : Can be made much more generic with the help of reflection (but has some golang limitations)
func (n *network) MarshalJSON() ([]byte, error) {
	netMap := make(map[string]interface{})
	netMap["name"] = n.name
	netMap["id"] = n.id
	netMap["created"] = n.created
	netMap["networkType"] = n.networkType
	netMap["scope"] = n.scope
	netMap["labels"] = n.labels
	netMap["ipamType"] = n.ipamType
	netMap["ipamOptions"] = n.ipamOptions
	netMap["addrSpace"] = n.addrSpace
	netMap["enableIPv6"] = n.enableIPv6
	if n.generic != nil {
		netMap["generic"] = n.generic
	}
	netMap["persist"] = n.persist
	netMap["postIPv6"] = n.postIPv6
	if len(n.ipamV4Config) > 0 {
		ics, err := json.Marshal(n.ipamV4Config)
		if err != nil {
			return nil, err
		}
		netMap["ipamV4Config"] = string(ics)
	}
	if len(n.ipamV4Info) > 0 {
		iis, err := json.Marshal(n.ipamV4Info)
		if err != nil {
			return nil, err
		}
		netMap["ipamV4Info"] = string(iis)
	}
	if len(n.ipamV6Config) > 0 {
		ics, err := json.Marshal(n.ipamV6Config)
		if err != nil {
			return nil, err
		}
		netMap["ipamV6Config"] = string(ics)
	}
	if len(n.ipamV6Info) > 0 {
		iis, err := json.Marshal(n.ipamV6Info)
		if err != nil {
			return nil, err
		}
		netMap["ipamV6Info"] = string(iis)
	}
	netMap["internal"] = n.internal
	netMap["attachable"] = n.attachable
	netMap["inDelete"] = n.inDelete
	netMap["ingress"] = n.ingress
	netMap["configOnly"] = n.configOnly
	netMap["configFrom"] = n.configFrom
	netMap["loadBalancerIP"] = n.loadBalancerIP
	netMap["loadBalancerMode"] = n.loadBalancerMode
	return json.Marshal(netMap)
}

// TODO : Can be made much more generic with the help of reflection (but has some golang limitations)
func (n *network) UnmarshalJSON(b []byte) (err error) {
	var netMap map[string]interface{}
	if err := json.Unmarshal(b, &netMap); err != nil {
		return err
	}
	n.name = netMap["name"].(string)
	n.id = netMap["id"].(string)
	// "created" is not available in older versions
	if v, ok := netMap["created"]; ok {
		// n.created is time.Time but marshalled as string
		if err = n.created.UnmarshalText([]byte(v.(string))); err != nil {
			logrus.Warnf("failed to unmarshal creation time %v: %v", v, err)
			n.created = time.Time{}
		}
	}
	n.networkType = netMap["networkType"].(string)
	n.enableIPv6 = netMap["enableIPv6"].(bool)

	// if we weren't unmarshaling to netMap we could simply set n.labels
	// unfortunately, we can't because map[string]interface{} != map[string]string
	if labels, ok := netMap["labels"].(map[string]interface{}); ok {
		n.labels = make(map[string]string, len(labels))
		for label, value := range labels {
			n.labels[label] = value.(string)
		}
	}

	if v, ok := netMap["ipamOptions"]; ok {
		if iOpts, ok := v.(map[string]interface{}); ok {
			n.ipamOptions = make(map[string]string, len(iOpts))
			for k, v := range iOpts {
				n.ipamOptions[k] = v.(string)
			}
		}
	}

	if v, ok := netMap["generic"]; ok {
		n.generic = v.(map[string]interface{})
		// Restore opts in their map[string]string form
		if v, ok := n.generic[netlabel.GenericData]; ok {
			var lmap map[string]string
			ba, err := json.Marshal(v)
			if err != nil {
				return err
			}
			if err := json.Unmarshal(ba, &lmap); err != nil {
				return err
			}
			n.generic[netlabel.GenericData] = lmap
		}
	}
	if v, ok := netMap["persist"]; ok {
		n.persist = v.(bool)
	}
	if v, ok := netMap["postIPv6"]; ok {
		n.postIPv6 = v.(bool)
	}
	if v, ok := netMap["ipamType"]; ok {
		n.ipamType = v.(string)
	} else {
		n.ipamType = ipamapi.DefaultIPAM
	}
	if v, ok := netMap["addrSpace"]; ok {
		n.addrSpace = v.(string)
	}
	if v, ok := netMap["ipamV4Config"]; ok {
		if err := json.Unmarshal([]byte(v.(string)), &n.ipamV4Config); err != nil {
			return err
		}
	}
	if v, ok := netMap["ipamV4Info"]; ok {
		if err := json.Unmarshal([]byte(v.(string)), &n.ipamV4Info); err != nil {
			return err
		}
	}
	if v, ok := netMap["ipamV6Config"]; ok {
		if err := json.Unmarshal([]byte(v.(string)), &n.ipamV6Config); err != nil {
			return err
		}
	}
	if v, ok := netMap["ipamV6Info"]; ok {
		if err := json.Unmarshal([]byte(v.(string)), &n.ipamV6Info); err != nil {
			return err
		}
	}
	if v, ok := netMap["internal"]; ok {
		n.internal = v.(bool)
	}
	if v, ok := netMap["attachable"]; ok {
		n.attachable = v.(bool)
	}
	if s, ok := netMap["scope"]; ok {
		n.scope = s.(string)
	}
	if v, ok := netMap["inDelete"]; ok {
		n.inDelete = v.(bool)
	}
	if v, ok := netMap["ingress"]; ok {
		n.ingress = v.(bool)
	}
	if v, ok := netMap["configOnly"]; ok {
		n.configOnly = v.(bool)
	}
	if v, ok := netMap["configFrom"]; ok {
		n.configFrom = v.(string)
	}
	if v, ok := netMap["loadBalancerIP"]; ok {
		n.loadBalancerIP = net.ParseIP(v.(string))
	}
	n.loadBalancerMode = loadBalancerModeDefault
	if v, ok := netMap["loadBalancerMode"]; ok {
		n.loadBalancerMode = v.(string)
	}
	// Reconcile old networks with the recently added `--ipv6` flag
	if !n.enableIPv6 {
		n.enableIPv6 = len(n.ipamV6Info) > 0
	}
	return nil
}

// NetworkOption is an option setter function type used to pass various options to
// NewNetwork method. The various setter functions of type NetworkOption are
// provided by libnetwork, they look like NetworkOptionXXXX(...)
type NetworkOption func(n *network)

// NetworkOptionGeneric function returns an option setter for a Generic option defined
// in a Dictionary of Key-Value pair
func NetworkOptionGeneric(generic map[string]interface{}) NetworkOption {
	return func(n *network) {
		if n.generic == nil {
			n.generic = make(map[string]interface{})
		}
		if val, ok := generic[netlabel.EnableIPv6]; ok {
			n.enableIPv6 = val.(bool)
		}
		if val, ok := generic[netlabel.Internal]; ok {
			n.internal = val.(bool)
		}
		for k, v := range generic {
			n.generic[k] = v
		}
	}
}

// NetworkOptionIngress returns an option setter to indicate if a network is
// an ingress network.
func NetworkOptionIngress(ingress bool) NetworkOption {
	return func(n *network) {
		n.ingress = ingress
	}
}

// NetworkOptionPersist returns an option setter to set persistence policy for a network
func NetworkOptionPersist(persist bool) NetworkOption {
	return func(n *network) {
		n.persist = persist
	}
}

// NetworkOptionEnableIPv6 returns an option setter to explicitly configure IPv6
func NetworkOptionEnableIPv6(enableIPv6 bool) NetworkOption {
	return func(n *network) {
		if n.generic == nil {
			n.generic = make(map[string]interface{})
		}
		n.enableIPv6 = enableIPv6
		n.generic[netlabel.EnableIPv6] = enableIPv6
	}
}

// NetworkOptionInternalNetwork returns an option setter to config the network
// to be internal which disables default gateway service
func NetworkOptionInternalNetwork() NetworkOption {
	return func(n *network) {
		if n.generic == nil {
			n.generic = make(map[string]interface{})
		}
		n.internal = true
		n.generic[netlabel.Internal] = true
	}
}

// NetworkOptionAttachable returns an option setter to set attachable for a network
func NetworkOptionAttachable(attachable bool) NetworkOption {
	return func(n *network) {
		n.attachable = attachable
	}
}

// NetworkOptionScope returns an option setter to overwrite the network's scope.
// By default the network's scope is set to the network driver's datascope.
func NetworkOptionScope(scope string) NetworkOption {
	return func(n *network) {
		n.scope = scope
	}
}

// NetworkOptionIpam function returns an option setter for the ipam configuration for this network
func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf, opts map[string]string) NetworkOption {
	return func(n *network) {
		if ipamDriver != "" {
			n.ipamType = ipamDriver
			if ipamDriver == ipamapi.DefaultIPAM {
				n.ipamType = defaultIpamForNetworkType(n.Type())
			}
		}
		n.ipamOptions = opts
		n.addrSpace = addrSpace
		n.ipamV4Config = ipV4
		n.ipamV6Config = ipV6
	}
}

// NetworkOptionLBEndpoint function returns an option setter for the configuration of the load balancer endpoint for this network
func NetworkOptionLBEndpoint(ip net.IP) NetworkOption {
	return func(n *network) {
		n.loadBalancerIP = ip
	}
}

// NetworkOptionDriverOpts function returns an option setter for any driver parameter described by a map
func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
	return func(n *network) {
		if n.generic == nil {
			n.generic = make(map[string]interface{})
		}
		if opts == nil {
			opts = make(map[string]string)
		}
		// Store the options
		n.generic[netlabel.GenericData] = opts
	}
}

// NetworkOptionLabels function returns an option setter for labels specific to a network
func NetworkOptionLabels(labels map[string]string) NetworkOption {
	return func(n *network) {
		n.labels = labels
	}
}

// NetworkOptionDynamic function returns an option setter for dynamic option for a network
func NetworkOptionDynamic() NetworkOption {
	return func(n *network) {
		n.dynamic = true
	}
}

// NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
// It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
// to a container as combination of fixed-cidr-v6 + mac-address
// TODO: Remove this option setter once we support endpoint ipam options
func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
	return func(n *network) {
		n.postIPv6 = enable
	}
}

// NetworkOptionConfigOnly tells controller this network is
// a configuration only network. It serves as a configuration
// for other networks.
func NetworkOptionConfigOnly() NetworkOption {
	return func(n *network) {
		n.configOnly = true
	}
}

// NetworkOptionConfigFrom tells controller to pick the
// network configuration from a configuration only network
func NetworkOptionConfigFrom(name string) NetworkOption {
	return func(n *network) {
		n.configFrom = name
	}
}

func (n *network) processOptions(options ...NetworkOption) {
	for _, opt := range options {
		if opt != nil {
			opt(n)
		}
	}
}

type networkDeleteParams struct {
	rmLBEndpoint bool
}

// NetworkDeleteOption is a type for optional parameters to pass to the
// network.Delete() function.
type NetworkDeleteOption func(p *networkDeleteParams)

// NetworkDeleteOptionRemoveLB informs a network.Delete() operation that should
// remove the load balancer endpoint for this network.  Note that the Delete()
// method will automatically remove a load balancing endpoint for most networks
// when the network is otherwise empty.  However, this does not occur for some
// networks.  In particular, networks marked as ingress (which are supposed to
// be more permanent than other overlay networks) won't automatically remove
// the LB endpoint on Delete().  This method allows for explicit removal of
// such networks provided there are no other endpoints present in the network.
// If the network still has non-LB endpoints present, Delete() will not
// remove the LB endpoint and will return an error.
func NetworkDeleteOptionRemoveLB(p *networkDeleteParams) {
	p.rmLBEndpoint = true
}

func (n *network) resolveDriver(name string, load bool) (driverapi.Driver, driverapi.Capability, error) {
	c := n.getController()

	// Check if a driver for the specified network type is available
	d, cap := c.drvRegistry.Driver(name)
	if d == nil {
		if load {
			err := c.loadDriver(name)
			if err != nil {
				return nil, driverapi.Capability{}, err
			}

			d, cap = c.drvRegistry.Driver(name)
			if d == nil {
				return nil, driverapi.Capability{}, fmt.Errorf("could not resolve driver %s in registry", name)
			}
		} else {
			// don't fail if driver loading is not required
			return nil, driverapi.Capability{}, nil
		}
	}

	return d, cap, nil
}

func (n *network) driverIsMultihost() bool {
	_, cap, err := n.resolveDriver(n.networkType, true)
	if err != nil {
		return false
	}
	return cap.ConnectivityScope == datastore.GlobalScope
}

func (n *network) driver(load bool) (driverapi.Driver, error) {
	d, cap, err := n.resolveDriver(n.networkType, load)
	if err != nil {
		return nil, err
	}

	n.mu.Lock()
	// If load is not required, driver, cap and err may all be nil
	if n.scope == "" {
		n.scope = cap.DataScope
	}
	if n.dynamic {
		// If the network is dynamic, then it is swarm
		// scoped regardless of the backing driver.
		n.scope = datastore.SwarmScope
	}
	n.mu.Unlock()
	return d, nil
}

func (n *network) Delete(options ...NetworkDeleteOption) error {
	var params networkDeleteParams
	for _, opt := range options {
		opt(&params)
	}
	return n.delete(false, params.rmLBEndpoint)
}

// This function gets called in 3 ways:
//   - Delete() -- (false, false)
//     remove if endpoint count == 0 or endpoint count == 1 and
//     there is a load balancer IP
//   - Delete(libnetwork.NetworkDeleteOptionRemoveLB) -- (false, true)
//     remove load balancer and network if endpoint count == 1
//   - controller.networkCleanup() -- (true, true)
//     remove the network no matter what
func (n *network) delete(force bool, rmLBEndpoint bool) error {
	n.mu.Lock()
	c := n.ctrlr
	name := n.name
	id := n.id
	n.mu.Unlock()

	c.networkLocker.Lock(id)
	defer c.networkLocker.Unlock(id) //nolint:errcheck

	n, err := c.getNetworkFromStore(id)
	if err != nil {
		return &UnknownNetworkError{name: name, id: id}
	}

	// Only remove ingress on force removal or explicit LB endpoint removal
	if n.ingress && !force && !rmLBEndpoint {
		return &ActiveEndpointsError{name: n.name, id: n.id}
	}

	// Check that the network is empty
	var emptyCount uint64
	if n.hasLoadBalancerEndpoint() {
		emptyCount = 1
	}
	if !force && n.getEpCnt().EndpointCnt() > emptyCount {
		if n.configOnly {
			return types.ForbiddenErrorf("configuration network %q is in use", n.Name())
		}
		return &ActiveEndpointsError{name: n.name, id: n.id}
	}

	if n.hasLoadBalancerEndpoint() {
		// If we got to this point, then the following must hold:
		//  * force is true OR endpoint count == 1
		if err := n.deleteLoadBalancerSandbox(); err != nil {
			if !force {
				return err
			}
			// continue deletion when force is true even on error
			logrus.Warnf("Error deleting load balancer sandbox: %v", err)
		}
		// Reload the network from the store to update the epcnt.
		n, err = c.getNetworkFromStore(id)
		if err != nil {
			return &UnknownNetworkError{name: name, id: id}
		}
	}

	// Up to this point, errors that we returned were recoverable.
	// From here on, any errors leave us in an inconsistent state.
	// This is unfortunate, but there isn't a safe way to
	// reconstitute a load-balancer endpoint after removing it.

	// Mark the network for deletion
	n.inDelete = true
	if err = c.updateToStore(n); err != nil {
		return fmt.Errorf("error marking network %s (%s) for deletion: %v", n.Name(), n.ID(), err)
	}

	if n.ConfigFrom() != "" {
		if t, err := c.getConfigNetwork(n.ConfigFrom()); err == nil {
			if err := t.getEpCnt().DecEndpointCnt(); err != nil {
				logrus.Warnf("Failed to update reference count for configuration network %q on removal of network %q: %v",
					t.Name(), n.Name(), err)
			}
		} else {
			logrus.Warnf("Could not find configuration network %q during removal of network %q", n.configFrom, n.Name())
		}
	}

	if n.configOnly {
		goto removeFromStore
	}

	n.ipamRelease()
	if err = c.updateToStore(n); err != nil {
		logrus.Warnf("Failed to update store after ipam release for network %s (%s): %v", n.Name(), n.ID(), err)
	}

	// We are about to delete the network. Leave the gossip
	// cluster for the network to stop all incoming network
	// specific gossip updates before cleaning up all the service
	// bindings for the network. But cleanup service binding
	// before deleting the network from the store since service
	// bindings cleanup requires the network in the store.
	n.cancelDriverWatches()
	if err = n.leaveCluster(); err != nil {
		logrus.Errorf("Failed leaving network %s from the agent cluster: %v", n.Name(), err)
	}

	// Cleanup the service discovery for this network
	c.cleanupServiceDiscovery(n.ID())

	// Cleanup the load balancer. On Windows this call is required
	// to remove remote loadbalancers in VFP, and must be performed before
	// dataplane network deletion.
	if runtime.GOOS == "windows" {
		c.cleanupServiceBindings(n.ID())
	}

	// Delete the network from the dataplane
	if err = n.deleteNetwork(); err != nil {
		if !force {
			return err
		}
		logrus.Debugf("driver failed to delete stale network %s (%s): %v", n.Name(), n.ID(), err)
	}

removeFromStore:
	// deleteFromStore performs an atomic delete operation and the
	// network.epCnt will help prevent any possible
	// race between endpoint join and network delete
	if err = c.deleteFromStore(n.getEpCnt()); err != nil {
		if !force {
			return fmt.Errorf("error deleting network endpoint count from store: %v", err)
		}
		logrus.Debugf("Error deleting endpoint count from store for stale network %s (%s) for deletion: %v", n.Name(), n.ID(), err)
	}

	if err = c.deleteFromStore(n); err != nil {
		return fmt.Errorf("error deleting network from store: %v", err)
	}

	return nil
}

func (n *network) deleteNetwork() error {
	d, err := n.driver(true)
	if err != nil {
		return fmt.Errorf("failed deleting network: %v", err)
	}

	if err := d.DeleteNetwork(n.ID()); err != nil {
		// Forbidden Errors should be honored
		if _, ok := err.(types.ForbiddenError); ok {
			return err
		}

		if _, ok := err.(types.MaskableError); !ok {
			logrus.Warnf("driver error deleting network %s : %v", n.name, err)
		}
	}

	for _, resolver := range n.resolver {
		resolver.Stop()
	}
	return nil
}

func (n *network) addEndpoint(ep *Endpoint) error {
	d, err := n.driver(true)
	if err != nil {
		return fmt.Errorf("failed to add endpoint: %v", err)
	}

	err = d.CreateEndpoint(n.id, ep.id, ep.Interface(), ep.generic)
	if err != nil {
		return types.InternalErrorf("failed to create endpoint %s on network %s: %v",
			ep.Name(), n.Name(), err)
	}

	return nil
}

func (n *network) CreateEndpoint(name string, options ...EndpointOption) (*Endpoint, error) {
	var err error
	if !config.IsValidName(name) {
		return nil, ErrInvalidName(name)
	}

	if n.ConfigOnly() {
		return nil, types.ForbiddenErrorf("cannot create endpoint on configuration-only network")
	}

	if _, err = n.EndpointByName(name); err == nil {
		return nil, types.ForbiddenErrorf("endpoint with name %s already exists in network %s", name, n.Name())
	}

	n.ctrlr.networkLocker.Lock(n.id)
	defer n.ctrlr.networkLocker.Unlock(n.id) //nolint:errcheck

	return n.createEndpoint(name, options...)
}

func (n *network) createEndpoint(name string, options ...EndpointOption) (*Endpoint, error) {
	var err error

	ep := &Endpoint{name: name, generic: make(map[string]interface{}), iface: &endpointInterface{}}
	ep.id = stringid.GenerateRandomID()

	// Initialize ep.network with a possibly stale copy of n. We need this to get network from
	// store. But once we get it from store we will have the most uptodate copy possibly.
	ep.network = n
	ep.network, err = ep.getNetworkFromStore()
	if err != nil {
		logrus.Errorf("failed to get network during CreateEndpoint: %v", err)
		return nil, err
	}
	n = ep.network

	ep.processOptions(options...)

	for _, llIPNet := range ep.Iface().LinkLocalAddresses() {
		if !llIPNet.IP.IsLinkLocalUnicast() {
			return nil, types.BadRequestErrorf("invalid link local IP address: %v", llIPNet.IP)
		}
	}

	if opt, ok := ep.generic[netlabel.MacAddress]; ok {
		if mac, ok := opt.(net.HardwareAddr); ok {
			ep.iface.mac = mac
		}
	}

	ipam, capability, err := n.getController().getIPAMDriver(n.ipamType)
	if err != nil {
		return nil, err
	}

	if capability.RequiresMACAddress {
		if ep.iface.mac == nil {
			ep.iface.mac = netutils.GenerateRandomMAC()
		}
		if ep.ipamOptions == nil {
			ep.ipamOptions = make(map[string]string)
		}
		ep.ipamOptions[netlabel.MacAddress] = ep.iface.mac.String()
	}

	if err = ep.assignAddress(ipam, true, n.enableIPv6 && !n.postIPv6); err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			ep.releaseAddress()
		}
	}()

	if err = n.addEndpoint(ep); err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			if e := ep.deleteEndpoint(false); e != nil {
				logrus.Warnf("cleaning up endpoint failed %s : %v", name, e)
			}
		}
	}()

	// We should perform updateToStore call right after addEndpoint
	// in order to have iface properly configured
	if err = n.getController().updateToStore(ep); err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			if e := n.getController().deleteFromStore(ep); e != nil {
				logrus.Warnf("error rolling back endpoint %s from store: %v", name, e)
			}
		}
	}()

	if err = ep.assignAddress(ipam, false, n.enableIPv6 && n.postIPv6); err != nil {
		return nil, err
	}

	// Watch for service records
	n.getController().watchSvcRecord(ep)
	defer func() {
		if err != nil {
			n.getController().unWatchSvcRecord(ep)
		}
	}()

	// Increment endpoint count to indicate completion of endpoint addition
	if err = n.getEpCnt().IncEndpointCnt(); err != nil {
		return nil, err
	}

	return ep, nil
}

func (n *network) Endpoints() []*Endpoint {
	endpoints, err := n.getEndpointsFromStore()
	if err != nil {
		logrus.Error(err)
	}
	return endpoints
}

func (n *network) WalkEndpoints(walker EndpointWalker) {
	for _, e := range n.Endpoints() {
		if walker(e) {
			return
		}
	}
}

func (n *network) EndpointByName(name string) (*Endpoint, error) {
	if name == "" {
		return nil, ErrInvalidName(name)
	}
	var e *Endpoint

	s := func(current *Endpoint) bool {
		if current.Name() == name {
			e = current
			return true
		}
		return false
	}

	n.WalkEndpoints(s)

	if e == nil {
		return nil, ErrNoSuchEndpoint(name)
	}

	return e, nil
}

func (n *network) EndpointByID(id string) (*Endpoint, error) {
	if id == "" {
		return nil, ErrInvalidID(id)
	}

	ep, err := n.getEndpointFromStore(id)
	if err != nil {
		return nil, ErrNoSuchEndpoint(id)
	}

	return ep, nil
}

func (n *network) updateSvcRecord(ep *Endpoint, localEps []*Endpoint, isAdd bool) {
	var ipv6 net.IP
	epName := ep.Name()
	if iface := ep.Iface(); iface != nil && iface.Address() != nil {
		myAliases := ep.MyAliases()
		if iface.AddressIPv6() != nil {
			ipv6 = iface.AddressIPv6().IP
		}

		serviceID := ep.svcID
		if serviceID == "" {
			serviceID = ep.ID()
		}
		if isAdd {
			// If anonymous endpoint has an alias use the first alias
			// for ip->name mapping. Not having the reverse mapping
			// breaks some apps
			if ep.isAnonymous() {
				if len(myAliases) > 0 {
					n.addSvcRecords(ep.ID(), myAliases[0], serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
				}
			} else {
				n.addSvcRecords(ep.ID(), epName, serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
			}
			for _, alias := range myAliases {
				n.addSvcRecords(ep.ID(), alias, serviceID, iface.Address().IP, ipv6, false, "updateSvcRecord")
			}
		} else {
			if ep.isAnonymous() {
				if len(myAliases) > 0 {
					n.deleteSvcRecords(ep.ID(), myAliases[0], serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
				}
			} else {
				n.deleteSvcRecords(ep.ID(), epName, serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
			}
			for _, alias := range myAliases {
				n.deleteSvcRecords(ep.ID(), alias, serviceID, iface.Address().IP, ipv6, false, "updateSvcRecord")
			}
		}
	}
}

func addIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP) {
	reverseIP := netutils.ReverseIP(ip.String())
	ipMap.Insert(reverseIP, ipInfo{
		name:      name,
		serviceID: serviceID,
	})
}

func delIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP) {
	reverseIP := netutils.ReverseIP(ip.String())
	ipMap.Remove(reverseIP, ipInfo{
		name:      name,
		serviceID: serviceID,
	})
}

func addNameToIP(svcMap *setmatrix.SetMatrix, name, serviceID string, epIP net.IP) {
	// Since DNS name resolution is case-insensitive, Use the lower-case form
	// of the name as the key into svcMap
	lowerCaseName := strings.ToLower(name)
	svcMap.Insert(lowerCaseName, svcMapEntry{
		ip:        epIP.String(),
		serviceID: serviceID,
	})
}

func delNameToIP(svcMap *setmatrix.SetMatrix, name, serviceID string, epIP net.IP) {
	lowerCaseName := strings.ToLower(name)
	svcMap.Remove(lowerCaseName, svcMapEntry{
		ip:        epIP.String(),
		serviceID: serviceID,
	})
}

func (n *network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP, ipMapUpdate bool, method string) {
	// Do not add service names for ingress network as this is a
	// routing only network
	if n.ingress {
		return
	}
	networkID := n.ID()
	logrus.Debugf("%s (%.7s).addSvcRecords(%s, %s, %s, %t) %s sid:%s", eID, networkID, name, epIP, epIPv6, ipMapUpdate, method, serviceID)

	c := n.getController()
	c.mu.Lock()
	defer c.mu.Unlock()

	sr, ok := c.svcRecords[networkID]
	if !ok {
		sr = svcInfo{
			svcMap:     setmatrix.NewSetMatrix(),
			svcIPv6Map: setmatrix.NewSetMatrix(),
			ipMap:      setmatrix.NewSetMatrix(),
		}
		c.svcRecords[networkID] = sr
	}

	if ipMapUpdate {
		addIPToName(sr.ipMap, name, serviceID, epIP)
		if epIPv6 != nil {
			addIPToName(sr.ipMap, name, serviceID, epIPv6)
		}
	}

	addNameToIP(sr.svcMap, name, serviceID, epIP)
	if epIPv6 != nil {
		addNameToIP(sr.svcIPv6Map, name, serviceID, epIPv6)
	}
}

func (n *network) deleteSvcRecords(eID, name, serviceID string, epIP net.IP, epIPv6 net.IP, ipMapUpdate bool, method string) {
	// Do not delete service names from ingress network as this is a
	// routing only network
	if n.ingress {
		return
	}
	networkID := n.ID()
	logrus.Debugf("%s (%.7s).deleteSvcRecords(%s, %s, %s, %t) %s sid:%s ", eID, networkID, name, epIP, epIPv6, ipMapUpdate, method, serviceID)

	c := n.getController()
	c.mu.Lock()
	defer c.mu.Unlock()

	sr, ok := c.svcRecords[networkID]
	if !ok {
		return
	}

	if ipMapUpdate {
		delIPToName(sr.ipMap, name, serviceID, epIP)

		if epIPv6 != nil {
			delIPToName(sr.ipMap, name, serviceID, epIPv6)
		}
	}

	delNameToIP(sr.svcMap, name, serviceID, epIP)

	if epIPv6 != nil {
		delNameToIP(sr.svcIPv6Map, name, serviceID, epIPv6)
	}
}

func (n *network) getSvcRecords(ep *Endpoint) []etchosts.Record {
	n.mu.Lock()
	defer n.mu.Unlock()

	if ep == nil {
		return nil
	}

	var recs []etchosts.Record

	epName := ep.Name()

	n.ctrlr.mu.Lock()
	defer n.ctrlr.mu.Unlock()
	sr, ok := n.ctrlr.svcRecords[n.id]
	if !ok || sr.svcMap == nil {
		return nil
	}

	svcMapKeys := sr.svcMap.Keys()
	// Loop on service names on this network
	for _, k := range svcMapKeys {
		if strings.Split(k, ".")[0] == epName {
			continue
		}
		// Get all the IPs associated to this service
		mapEntryList, ok := sr.svcMap.Get(k)
		if !ok {
			// The key got deleted
			continue
		}
		if len(mapEntryList) == 0 {
			logrus.Warnf("Found empty list of IP addresses for service %s on network %s (%s)", k, n.name, n.id)
			continue
		}

		recs = append(recs, etchosts.Record{
			Hosts: k,
			IP:    mapEntryList[0].(svcMapEntry).ip,
		})
	}

	return recs
}

func (n *network) getController() *Controller {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.ctrlr
}

func (n *network) ipamAllocate() error {
	if n.hasSpecialDriver() {
		return nil
	}

	ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
	if err != nil {
		return err
	}

	if n.addrSpace == "" {
		if n.addrSpace, err = n.deriveAddressSpace(); err != nil {
			return err
		}
	}

	err = n.ipamAllocateVersion(4, ipam)
	if err != nil {
		return err
	}

	defer func() {
		if err != nil {
			n.ipamReleaseVersion(4, ipam)
		}
	}()

	if !n.enableIPv6 {
		return nil
	}

	err = n.ipamAllocateVersion(6, ipam)
	return err
}

func (n *network) requestPoolHelper(ipam ipamapi.Ipam, addressSpace, preferredPool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
	for {
		poolID, pool, meta, err := ipam.RequestPool(addressSpace, preferredPool, subPool, options, v6)
		if err != nil {
			return "", nil, nil, err
		}

		// If the network belongs to global scope or the pool was
		// explicitly chosen or it is invalid, do not perform the overlap check.
		if n.Scope() == datastore.GlobalScope || preferredPool != "" || !types.IsIPNetValid(pool) {
			return poolID, pool, meta, nil
		}

		// Check for overlap and if none found, we have found the right pool.
		if _, err := netutils.FindAvailableNetwork([]*net.IPNet{pool}); err == nil {
			return poolID, pool, meta, nil
		}

		// Pool obtained in this iteration is
		// overlapping. Hold onto the pool and don't release
		// it yet, because we don't want ipam to give us back
		// the same pool over again. But make sure we still do
		// a deferred release when we have either obtained a
		// non-overlapping pool or ran out of pre-defined
		// pools.
		defer func() {
			if err := ipam.ReleasePool(poolID); err != nil {
				logrus.Warnf("Failed to release overlapping pool %s while returning from pool request helper for network %s", pool, n.Name())
			}
		}()

		// If this is a preferred pool request and the network
		// is local scope and there is an overlap, we fail the
		// network creation right here. The pool will be
		// released in the defer.
		if preferredPool != "" {
			return "", nil, nil, fmt.Errorf("requested subnet %s overlaps in the host", preferredPool)
		}
	}
}

func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
	var (
		cfgList  *[]*IpamConf
		infoList *[]*IpamInfo
		err      error
	)

	switch ipVer {
	case 4:
		cfgList = &n.ipamV4Config
		infoList = &n.ipamV4Info
	case 6:
		cfgList = &n.ipamV6Config
		infoList = &n.ipamV6Info
	default:
		return types.InternalErrorf("incorrect ip version passed to ipam allocate: %d", ipVer)
	}

	if len(*cfgList) == 0 {
		*cfgList = []*IpamConf{{}}
	}

	*infoList = make([]*IpamInfo, len(*cfgList))

	logrus.Debugf("Allocating IPv%d pools for network %s (%s)", ipVer, n.Name(), n.ID())

	for i, cfg := range *cfgList {
		if err = cfg.Validate(); err != nil {
			return err
		}
		d := &IpamInfo{}
		(*infoList)[i] = d

		d.AddressSpace = n.addrSpace
		d.PoolID, d.Pool, d.Meta, err = n.requestPoolHelper(ipam, n.addrSpace, cfg.PreferredPool, cfg.SubPool, n.ipamOptions, ipVer == 6)
		if err != nil {
			return err
		}

		defer func() {
			if err != nil {
				if err := ipam.ReleasePool(d.PoolID); err != nil {
					logrus.Warnf("Failed to release address pool %s after failure to create network %s (%s)", d.PoolID, n.Name(), n.ID())
				}
			}
		}()

		if gws, ok := d.Meta[netlabel.Gateway]; ok {
			if d.Gateway, err = types.ParseCIDR(gws); err != nil {
				return types.BadRequestErrorf("failed to parse gateway address (%v) returned by ipam driver: %v", gws, err)
			}
		}

		// If user requested a specific gateway, libnetwork will allocate it
		// irrespective of whether ipam driver returned a gateway already.
		// If none of the above is true, libnetwork will allocate one.
		if cfg.Gateway != "" || d.Gateway == nil {
			var gatewayOpts = map[string]string{
				ipamapi.RequestAddressType: netlabel.Gateway,
			}
			if d.Gateway, _, err = ipam.RequestAddress(d.PoolID, net.ParseIP(cfg.Gateway), gatewayOpts); err != nil {
				return types.InternalErrorf("failed to allocate gateway (%v): %v", cfg.Gateway, err)
			}
		}

		// Auxiliary addresses must be part of the master address pool
		// If they fall into the container addressable pool, libnetwork will reserve them
		if cfg.AuxAddresses != nil {
			var ip net.IP
			d.IPAMData.AuxAddresses = make(map[string]*net.IPNet, len(cfg.AuxAddresses))
			for k, v := range cfg.AuxAddresses {
				if ip = net.ParseIP(v); ip == nil {
					return types.BadRequestErrorf("non parsable secondary ip address (%s:%s) passed for network %s", k, v, n.Name())
				}
				if !d.Pool.Contains(ip) {
					return types.ForbiddenErrorf("auxiliary address: (%s:%s) must belong to the master pool: %s", k, v, d.Pool)
				}
				// Attempt reservation in the container addressable pool, silent the error if address does not belong to that pool
				if d.IPAMData.AuxAddresses[k], _, err = ipam.RequestAddress(d.PoolID, ip, nil); err != nil && err != ipamapi.ErrIPOutOfRange {
					return types.InternalErrorf("failed to allocate secondary ip address (%s:%s): %v", k, v, err)
				}
			}
		}
	}

	return nil
}

func (n *network) ipamRelease() {
	if n.hasSpecialDriver() {
		return
	}
	ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
	if err != nil {
		logrus.Warnf("Failed to retrieve ipam driver to release address pool(s) on delete of network %s (%s): %v", n.Name(), n.ID(), err)
		return
	}
	n.ipamReleaseVersion(4, ipam)
	n.ipamReleaseVersion(6, ipam)
}

func (n *network) ipamReleaseVersion(ipVer int, ipam ipamapi.Ipam) {
	var infoList *[]*IpamInfo

	switch ipVer {
	case 4:
		infoList = &n.ipamV4Info
	case 6:
		infoList = &n.ipamV6Info
	default:
		logrus.Warnf("incorrect ip version passed to ipam release: %d", ipVer)
		return
	}

	if len(*infoList) == 0 {
		return
	}

	logrus.Debugf("releasing IPv%d pools from network %s (%s)", ipVer, n.Name(), n.ID())

	for _, d := range *infoList {
		if d.Gateway != nil {
			if err := ipam.ReleaseAddress(d.PoolID, d.Gateway.IP); err != nil {
				logrus.Warnf("Failed to release gateway ip address %s on delete of network %s (%s): %v", d.Gateway.IP, n.Name(), n.ID(), err)
			}
		}
		if d.IPAMData.AuxAddresses != nil {
			for k, nw := range d.IPAMData.AuxAddresses {
				if d.Pool.Contains(nw.IP) {
					if err := ipam.ReleaseAddress(d.PoolID, nw.IP); err != nil && err != ipamapi.ErrIPOutOfRange {
						logrus.Warnf("Failed to release secondary ip address %s (%v) on delete of network %s (%s): %v", k, nw.IP, n.Name(), n.ID(), err)
					}
				}
			}
		}
		if err := ipam.ReleasePool(d.PoolID); err != nil {
			logrus.Warnf("Failed to release address pool %s on delete of network %s (%s): %v", d.PoolID, n.Name(), n.ID(), err)
		}
	}

	*infoList = nil
}

func (n *network) getIPInfo(ipVer int) []*IpamInfo {
	var info []*IpamInfo
	switch ipVer {
	case 4:
		info = n.ipamV4Info
	case 6:
		info = n.ipamV6Info
	default:
		return nil
	}
	l := make([]*IpamInfo, 0, len(info))
	n.mu.Lock()
	l = append(l, info...)
	n.mu.Unlock()
	return l
}

func (n *network) getIPData(ipVer int) []driverapi.IPAMData {
	var info []*IpamInfo
	switch ipVer {
	case 4:
		info = n.ipamV4Info
	case 6:
		info = n.ipamV6Info
	default:
		return nil
	}
	l := make([]driverapi.IPAMData, 0, len(info))
	n.mu.Lock()
	for _, d := range info {
		l = append(l, d.IPAMData)
	}
	n.mu.Unlock()
	return l
}

func (n *network) deriveAddressSpace() (string, error) {
	ipam, _ := n.getController().ipamRegistry.IPAM(n.ipamType)
	if ipam == nil {
		return "", types.NotFoundErrorf("failed to get default address space: unknown ipam type %q", n.ipamType)
	}
	local, global, err := ipam.GetDefaultAddressSpaces()
	if err != nil {
		return "", types.NotFoundErrorf("failed to get default address space: %v", err)
	}
	if n.DataScope() == datastore.GlobalScope {
		return global, nil
	}
	return local, nil
}

func (n *network) Info() NetworkInfo {
	return n
}

func (n *network) Peers() []networkdb.PeerInfo {
	if !n.Dynamic() {
		return []networkdb.PeerInfo{}
	}

	agent := n.getController().getAgent()
	if agent == nil {
		return []networkdb.PeerInfo{}
	}

	return agent.networkDB.Peers(n.ID())
}

func (n *network) DriverOptions() map[string]string {
	n.mu.Lock()
	defer n.mu.Unlock()
	if n.generic != nil {
		if m, ok := n.generic[netlabel.GenericData]; ok {
			return m.(map[string]string)
		}
	}
	return map[string]string{}
}

func (n *network) Scope() string {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.scope
}

func (n *network) IpamConfig() (string, map[string]string, []*IpamConf, []*IpamConf) {
	n.mu.Lock()
	defer n.mu.Unlock()

	v4L := make([]*IpamConf, len(n.ipamV4Config))
	v6L := make([]*IpamConf, len(n.ipamV6Config))

	for i, c := range n.ipamV4Config {
		cc := &IpamConf{}
		if err := c.CopyTo(cc); err != nil {
			logrus.WithError(err).Error("Error copying ipam ipv4 config")
		}
		v4L[i] = cc
	}

	for i, c := range n.ipamV6Config {
		cc := &IpamConf{}
		if err := c.CopyTo(cc); err != nil {
			logrus.WithError(err).Debug("Error copying ipam ipv6 config")
		}
		v6L[i] = cc
	}

	return n.ipamType, n.ipamOptions, v4L, v6L
}

func (n *network) IpamInfo() ([]*IpamInfo, []*IpamInfo) {
	n.mu.Lock()
	defer n.mu.Unlock()

	v4Info := make([]*IpamInfo, len(n.ipamV4Info))
	v6Info := make([]*IpamInfo, len(n.ipamV6Info))

	for i, info := range n.ipamV4Info {
		ic := &IpamInfo{}
		if err := info.CopyTo(ic); err != nil {
			logrus.WithError(err).Error("Error copying ipv4 ipam config")
		}
		v4Info[i] = ic
	}

	for i, info := range n.ipamV6Info {
		ic := &IpamInfo{}
		if err := info.CopyTo(ic); err != nil {
			logrus.WithError(err).Error("Error copying ipv6 ipam config")
		}
		v6Info[i] = ic
	}

	return v4Info, v6Info
}

func (n *network) Internal() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.internal
}

func (n *network) Attachable() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.attachable
}

func (n *network) Ingress() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.ingress
}

func (n *network) Dynamic() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.dynamic
}

func (n *network) IPv6Enabled() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.enableIPv6
}

func (n *network) ConfigFrom() string {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.configFrom
}

func (n *network) ConfigOnly() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.configOnly
}

func (n *network) Labels() map[string]string {
	n.mu.Lock()
	defer n.mu.Unlock()

	var lbls = make(map[string]string, len(n.labels))
	for k, v := range n.labels {
		lbls[k] = v
	}

	return lbls
}

func (n *network) TableEventRegister(tableName string, objType driverapi.ObjectType) error {
	if !driverapi.IsValidType(objType) {
		return fmt.Errorf("invalid object type %v in registering table, %s", objType, tableName)
	}

	t := networkDBTable{
		name:    tableName,
		objType: objType,
	}
	n.mu.Lock()
	defer n.mu.Unlock()
	n.driverTables = append(n.driverTables, t)
	return nil
}

func (n *network) UpdateIpamConfig(ipV4Data []driverapi.IPAMData) {
	ipamV4Config := make([]*IpamConf, len(ipV4Data))

	for i, data := range ipV4Data {
		ic := &IpamConf{}
		ic.PreferredPool = data.Pool.String()
		ic.Gateway = data.Gateway.IP.String()
		ipamV4Config[i] = ic
	}

	n.mu.Lock()
	defer n.mu.Unlock()
	n.ipamV4Config = ipamV4Config
}

// Special drivers are ones which do not need to perform any network plumbing
func (n *network) hasSpecialDriver() bool {
	return n.Type() == "host" || n.Type() == "null"
}

func (n *network) hasLoadBalancerEndpoint() bool {
	return len(n.loadBalancerIP) != 0
}

func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) {
	var ipv6Miss bool

	c := n.getController()
	networkID := n.ID()
	c.mu.Lock()
	defer c.mu.Unlock()
	sr, ok := c.svcRecords[networkID]

	if !ok {
		return nil, false
	}

	req = strings.TrimSuffix(req, ".")
	req = strings.ToLower(req)
	ipSet, ok := sr.svcMap.Get(req)

	if ipType == types.IPv6 {
		// If the name resolved to v4 address then its a valid name in
		// the docker network domain. If the network is not v6 enabled
		// set ipv6Miss to filter the DNS query from going to external
		// resolvers.
		if ok && !n.enableIPv6 {
			ipv6Miss = true
		}
		ipSet, ok = sr.svcIPv6Map.Get(req)
	}

	if ok && len(ipSet) > 0 {
		// this map is to avoid IP duplicates, this can happen during a transition period where 2 services are using the same IP
		noDup := make(map[string]bool)
		var ipLocal []net.IP
		for _, ip := range ipSet {
			if _, dup := noDup[ip.(svcMapEntry).ip]; !dup {
				noDup[ip.(svcMapEntry).ip] = true
				ipLocal = append(ipLocal, net.ParseIP(ip.(svcMapEntry).ip))
			}
		}
		return ipLocal, ok
	}

	return nil, ipv6Miss
}

func (n *network) HandleQueryResp(name string, ip net.IP) {
	networkID := n.ID()
	c := n.getController()
	c.mu.Lock()
	defer c.mu.Unlock()
	sr, ok := c.svcRecords[networkID]

	if !ok {
		return
	}

	ipStr := netutils.ReverseIP(ip.String())
	// If an object with extResolver == true is already in the set this call will fail
	// but anyway it means that has already been inserted before
	if ok, _ := sr.ipMap.Contains(ipStr, ipInfo{name: name}); ok {
		sr.ipMap.Remove(ipStr, ipInfo{name: name})
		sr.ipMap.Insert(ipStr, ipInfo{name: name, extResolver: true})
	}
}

func (n *network) ResolveIP(ip string) string {
	networkID := n.ID()
	c := n.getController()
	c.mu.Lock()
	defer c.mu.Unlock()
	sr, ok := c.svcRecords[networkID]

	if !ok {
		return ""
	}

	nwName := n.Name()

	elemSet, ok := sr.ipMap.Get(ip)
	if !ok || len(elemSet) == 0 {
		return ""
	}
	// NOTE it is possible to have more than one element in the Set, this will happen
	// because of interleave of different events from different sources (local container create vs
	// network db notifications)
	// In such cases the resolution will be based on the first element of the set, and can vary
	// during the system stabilitation
	elem, ok := elemSet[0].(ipInfo)
	if !ok {
		setStr, b := sr.ipMap.String(ip)
		logrus.Errorf("expected set of ipInfo type for key %s set:%t %s", ip, b, setStr)
		return ""
	}

	if elem.extResolver {
		return ""
	}

	return elem.name + "." + nwName
}

func (n *network) ResolveService(name string) ([]*net.SRV, []net.IP) {
	c := n.getController()

	srv := []*net.SRV{}
	ip := []net.IP{}

	logrus.Debugf("Service name To resolve: %v", name)

	// There are DNS implementations that allow SRV queries for names not in
	// the format defined by RFC 2782. Hence specific validations checks are
	// not done
	parts := strings.Split(name, ".")
	if len(parts) < 3 {
		return nil, nil
	}

	portName := parts[0]
	proto := parts[1]
	svcName := strings.Join(parts[2:], ".")

	networkID := n.ID()
	c.mu.Lock()
	defer c.mu.Unlock()
	sr, ok := c.svcRecords[networkID]

	if !ok {
		return nil, nil
	}

	svcs, ok := sr.service[svcName]
	if !ok {
		return nil, nil
	}

	for _, svc := range svcs {
		if svc.portName != portName {
			continue
		}
		if svc.proto != proto {
			continue
		}
		for _, t := range svc.target {
			srv = append(srv,
				&net.SRV{
					Target: t.name,
					Port:   t.port,
				})

			ip = append(ip, t.ip)
		}
	}

	return srv, ip
}

func (n *network) ExecFunc(f func()) error {
	return types.NotImplementedErrorf("ExecFunc not supported by network")
}

func (n *network) NdotsSet() bool {
	return false
}

// config-only network is looked up by name
func (c *Controller) getConfigNetwork(name string) (*network, error) {
	var n Network

	s := func(current Network) bool {
		if current.Info().ConfigOnly() && current.Name() == name {
			n = current
			return true
		}
		return false
	}

	c.WalkNetworks(s)

	if n == nil {
		return nil, types.NotFoundErrorf("configuration network %q not found", name)
	}

	return n.(*network), nil
}

func (n *network) lbSandboxName() string {
	name := "lb-" + n.name
	if n.ingress {
		name = n.name + "-sbox"
	}
	return name
}

func (n *network) lbEndpointName() string {
	return n.name + "-endpoint"
}

func (n *network) createLoadBalancerSandbox() (retErr error) {
	sandboxName := n.lbSandboxName()
	// Mark the sandbox to be a load balancer
	sbOptions := []SandboxOption{OptionLoadBalancer(n.id)}
	if n.ingress {
		sbOptions = append(sbOptions, OptionIngress())
	}
	sb, err := n.ctrlr.NewSandbox(sandboxName, sbOptions...)
	if err != nil {
		return err
	}
	defer func() {
		if retErr != nil {
			if e := n.ctrlr.SandboxDestroy(sandboxName); e != nil {
				logrus.Warnf("could not delete sandbox %s on failure on failure (%v): %v", sandboxName, retErr, e)
			}
		}
	}()

	endpointName := n.lbEndpointName()
	epOptions := []EndpointOption{
		CreateOptionIpam(n.loadBalancerIP, nil, nil, nil),
		CreateOptionLoadBalancer(),
	}
	if n.hasLoadBalancerEndpoint() && !n.ingress {
		// Mark LB endpoints as anonymous so they don't show up in DNS
		epOptions = append(epOptions, CreateOptionAnonymous())
	}
	ep, err := n.createEndpoint(endpointName, epOptions...)
	if err != nil {
		return err
	}
	defer func() {
		if retErr != nil {
			if e := ep.Delete(true); e != nil {
				logrus.Warnf("could not delete endpoint %s on failure on failure (%v): %v", endpointName, retErr, e)
			}
		}
	}()

	if err := ep.Join(sb, nil); err != nil {
		return err
	}

	return sb.EnableService()
}

func (n *network) deleteLoadBalancerSandbox() error {
	n.mu.Lock()
	c := n.ctrlr
	name := n.name
	n.mu.Unlock()

	sandboxName := n.lbSandboxName()
	endpointName := n.lbEndpointName()

	endpoint, err := n.EndpointByName(endpointName)
	if err != nil {
		logrus.Warnf("Failed to find load balancer endpoint %s on network %s: %v", endpointName, name, err)
	} else {
		info := endpoint.Info()
		if info != nil {
			sb := info.Sandbox()
			if sb != nil {
				if err := sb.DisableService(); err != nil {
					logrus.Warnf("Failed to disable service on sandbox %s: %v", sandboxName, err)
					// Ignore error and attempt to delete the load balancer endpoint
				}
			}
		}

		if err := endpoint.Delete(true); err != nil {
			logrus.Warnf("Failed to delete endpoint %s (%s) in %s: %v", endpoint.Name(), endpoint.ID(), sandboxName, err)
			// Ignore error and attempt to delete the sandbox.
		}
	}

	if err := c.SandboxDestroy(sandboxName); err != nil {
		return fmt.Errorf("Failed to delete %s sandbox: %v", sandboxName, err)
	}
	return nil
}

package libnetwork

import (
	"encoding/json"
	"fmt"
	"net"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/docker/docker/libnetwork/config"
	"github.com/docker/docker/libnetwork/datastore"
	"github.com/docker/docker/libnetwork/driverapi"
	"github.com/docker/docker/libnetwork/etchosts"
	"github.com/docker/docker/libnetwork/internal/setmatrix"
	"github.com/docker/docker/libnetwork/ipamapi"
	"github.com/docker/docker/libnetwork/netlabel"
	"github.com/docker/docker/libnetwork/netutils"
	"github.com/docker/docker/libnetwork/networkdb"
	"github.com/docker/docker/libnetwork/options"
	"github.com/docker/docker/libnetwork/types"
	"github.com/docker/docker/pkg/stringid"
	"github.com/sirupsen/logrus"
)

// A Network represents a logical connectivity zone that containers may
// join using the Link method. A Network is managed by a specific driver.
type Network interface {
	// Name returns a user chosen name for this network.
	Name() string

	// ID returns a system generated id for this network.
	ID() string

	// Type returns the type of network, which corresponds to its managing driver.
	Type() string

	// CreateEndpoint creates a new endpoint to this network symbolically identified by the
	// specified unique name. The options parameter carries driver specific options.
	CreateEndpoint(name string, options ...EndpointOption) (*Endpoint, error)

	// Delete the network.
	Delete(options ...NetworkDeleteOption) error

	// Endpoints returns the list of Endpoint(s) in this network.
	Endpoints() []*Endpoint

	// WalkEndpoints uses the provided function to walk the Endpoints.
	WalkEndpoints(walker EndpointWalker)

	// EndpointByName returns the Endpoint which has the passed name. If not found, the error ErrNoSuchEndpoint is returned.
	EndpointByName(name string) (*Endpoint, error)

	// EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned.
	EndpointByID(id string) (*Endpoint, error)

	// Info returns certain operational data belonging to this network.
	Info() NetworkInfo
}

// NetworkInfo returns some configuration and operational information about the network
type NetworkInfo interface {
	IpamConfig() (string, map[string]string, []*IpamConf, []*IpamConf)
	IpamInfo() ([]*IpamInfo, []*IpamInfo)
	DriverOptions() map[string]string
	Scope() string
	IPv6Enabled() bool
	Internal() bool
	Attachable() bool
	Ingress() bool
	ConfigFrom() string
	ConfigOnly() bool
	Labels() map[string]string
	Dynamic() bool
	Created() time.Time
	// Peers returns a slice of PeerInfo structures which has the information about the peer
	// nodes participating in the same overlay network. This is currently the per-network
	// gossip cluster. For non-dynamic overlay networks and bridge networks it returns an
	// empty slice
	Peers() []networkdb.PeerInfo
	// Services returns a map of services keyed by the service name with the details
	// of all the tasks that belong to the service. Applicable only in swarm mode.
	Services() map[string]ServiceInfo
}

// EndpointWalker is a client provided function which will be used to walk the Endpoints.
// When the function returns true, the walk will stop.
type EndpointWalker func(ep *Endpoint) bool

// ipInfo is the reverse mapping from IP to service name to serve the PTR query.
// extResolver is set if an external server resolves a service name to this IP.
// It's an indication to defer PTR queries also to that external server.
type ipInfo struct {
	name        string
	serviceID   string
	extResolver bool
}

// svcMapEntry is the body of the element into the svcMap
// The ip is a string because the SetMatrix does not accept non hashable values
type svcMapEntry struct {
	ip        string
	serviceID string
}

type svcInfo struct {
	svcMap     setmatrix.SetMatrix[svcMapEntry]
	svcIPv6Map setmatrix.SetMatrix[svcMapEntry]
	ipMap      setmatrix.SetMatrix[ipInfo]
	service    map[string][]servicePorts
}

// backing container or host's info
type serviceTarget struct {
	name string
	ip   net.IP
	port uint16
}

type servicePorts struct {
	portName string
	proto    string
	target   []serviceTarget
}

type networkDBTable struct {
	name    string
	objType driverapi.ObjectType
}

// IpamConf contains all the ipam related configurations for a network
type IpamConf struct {
	// PreferredPool is the master address pool for containers and network interfaces.
	PreferredPool string
	// SubPool is a subset of the master pool. If specified,
	// this becomes the container pool.
	SubPool string
	// Gateway is the preferred Network Gateway address (optional).
	Gateway string
	// AuxAddresses contains auxiliary addresses for network driver. Must be within the master pool.
	// libnetwork will reserve them if they fall into the container pool.
	AuxAddresses map[string]string
}

// Validate checks whether the configuration is valid
func (c *IpamConf) Validate() error {
	if c.Gateway != "" && nil == net.ParseIP(c.Gateway) {
		return types.BadRequestErrorf("invalid gateway address %s in Ipam configuration", c.Gateway)
	}
	return nil
}

// IpamInfo contains all the ipam related operational info for a network
type IpamInfo struct {
	PoolID string
	Meta   map[string]string
	driverapi.IPAMData
}

// MarshalJSON encodes IpamInfo into json message
func (i *IpamInfo) MarshalJSON() ([]byte, error) {
	m := map[string]interface{}{
		"PoolID": i.PoolID,
	}
	v, err := json.Marshal(&i.IPAMData)
	if err != nil {
		return nil, err
	}
	m["IPAMData"] = string(v)

	if i.Meta != nil {
		m["Meta"] = i.Meta
	}
	return json.Marshal(m)
}

// UnmarshalJSON decodes json message into PoolData
func (i *IpamInfo) UnmarshalJSON(data []byte) error {
	var (
		m   map[string]interface{}
		err error
	)
	if err = json.Unmarshal(data, &m); err != nil {
		return err
	}
	i.PoolID = m["PoolID"].(string)
	if v, ok := m["Meta"]; ok {
		b, _ := json.Marshal(v)
		if err = json.Unmarshal(b, &i.Meta); err != nil {
			return err
		}
	}
	if v, ok := m["IPAMData"]; ok {
		if err = json.Unmarshal([]byte(v.(string)), &i.IPAMData); err != nil {
			return err
		}
	}
	return nil
}

type network struct {
	ctrlr            *Controller
	name             string
	networkType      string
	id               string
	created          time.Time
	scope            string // network data scope
	labels           map[string]string
	ipamType         string
	ipamOptions      map[string]string
	addrSpace        string
	ipamV4Config     []*IpamConf
	ipamV6Config     []*IpamConf
	ipamV4Info       []*IpamInfo
	ipamV6Info       []*IpamInfo
	enableIPv6       bool
	postIPv6         bool
	epCnt            *endpointCnt
	generic          options.Generic
	dbIndex          uint64
	dbExists         bool
	persist          bool
	drvOnce          *sync.Once
	resolverOnce     sync.Once //nolint:nolintlint,unused // only used on windows
	resolver         []*Resolver
	internal         bool
	attachable       bool
	inDelete         bool
	ingress          bool
	driverTables     []networkDBTable
	dynamic          bool
	configOnly       bool
	configFrom       string
	loadBalancerIP   net.IP
	loadBalancerMode string
	mu               sync.Mutex
}

const (
	loadBalancerModeNAT     = "NAT"
	loadBalancerModeDSR     = "DSR"
	loadBalancerModeDefault = loadBalancerModeNAT
)

func (n *network) Name() string {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.name
}

func (n *network) ID() string {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.id
}

func (n *network) Created() time.Time {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.created
}

func (n *network) Type() string {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.networkType
}

func (n *network) Key() []string {
	n.mu.Lock()
	defer n.mu.Unlock()
	return []string{datastore.NetworkKeyPrefix, n.id}
}

func (n *network) KeyPrefix() []string {
	return []string{datastore.NetworkKeyPrefix}
}

func (n *network) Value() []byte {
	n.mu.Lock()
	defer n.mu.Unlock()
	b, err := json.Marshal(n)
	if err != nil {
		return nil
	}
	return b
}

func (n *network) SetValue(value []byte) error {
	return json.Unmarshal(value, n)
}

func (n *network) Index() uint64 {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.dbIndex
}

func (n *network) SetIndex(index uint64) {
	n.mu.Lock()
	n.dbIndex = index
	n.dbExists = true
	n.mu.Unlock()
}

func (n *network) Exists() bool {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.dbExists
}

func (n *network) Skip() bool {
	n.mu.Lock()
	defer n.mu.Unlock()
	return !n.persist
}

func (n *network) New() datastore.KVObject {
	n.mu.Lock()
	defer n.mu.Unlock()

	return &network{
		ctrlr:   n.ctrlr,
		drvOnce: &sync.Once{},
		scope:   n.scope,
	}
}

// CopyTo deep copies to the destination IpamConfig
func (c *IpamConf) CopyTo(dstC *IpamConf) error {
	dstC.PreferredPool = c.PreferredPool
	dstC.SubPool = c.SubPool
	dstC.Gateway = c.Gateway
	if c.AuxAddresses != nil {
		dstC.AuxAddresses = make(map[string]string, len(c.AuxAddresses))
		for k, v := range c.AuxAddresses {
			dstC.AuxAddresses[k] = v
		}
	}
	return nil
}

// CopyTo deep copies to the destination IpamInfo
func (i *IpamInfo) CopyTo(dstI *IpamInfo) error {
	dstI.PoolID = i.PoolID
	if i.Meta != nil {
		dstI.Meta = make(map[string]string)
		for k, v := range i.Meta {
			dstI.Meta[k] = v
		}
	}

	dstI.AddressSpace = i.AddressSpace
	dstI.Pool = types.GetIPNetCopy(i.Pool)
	dstI.Gateway = types.GetIPNetCopy(i.Gateway)

	if i.AuxAddresses != nil {
		dstI.AuxAddresses = make(map[string]*net.IPNet)
		for k, v := range i.AuxAddresses {
			dstI.AuxAddresses[k] = types.GetIPNetCopy(v)
		}
	}

	return nil
}

func (n *network) validateConfiguration() error {
	if n.configOnly {
		// Only supports network specific configurations.
		// Network operator configurations are not supported.
		if n.ingress || n.internal || n.attachable || n.scope != "" {
			return types.ForbiddenErrorf("configuration network can only contain network " +
				"specific fields. Network operator fields like " +
				"[ ingress | internal | attachable | scope ] are not supported.")
		}
	}
	if n.configFrom != "" {
		if n.configOnly {
			return types.ForbiddenErrorf("a configuration network cannot depend on another configuration network")
		}
		if n.ipamType != "" &&
			n.ipamType != defaultIpamForNetworkType(n.networkType) ||
			n.enableIPv6 ||
			len(n.labels) > 0 || len(n.ipamOptions) > 0 ||
			len(n.ipamV4Config) > 0 || len(n.ipamV6Config) > 0 {
			return types.ForbiddenErrorf("user specified configurations are not supported if the network depends on a configuration network")
		}
		if len(n.generic) > 0 {
			if data, ok := n.generic[netlabel.GenericData]; ok {
				var (
					driverOptions map[string]string
					opts          interface{}
				)
				switch t := data.(type) {
				case map[string]interface{}, map[string]string:
					opts = t
				}
				ba, err := json.Marshal(opts)
				if err != nil {
					return fmt.Errorf("failed to validate network configuration: %v", err)
				}
				if err := json.Unmarshal(ba, &driverOptions); err != nil {
					return fmt.Errorf("failed to validate network configuration: %v", err)
				}
				if len(driverOptions) > 0 {
					return types.ForbiddenErrorf("network driver options are not supported if the network depends on a configuration network")
				}
			}
		}
	}
	return nil
}

// applyConfigurationTo applies network specific configurations.
func (n *network) applyConfigurationTo(to *network) error {
	to.enableIPv6 = n.enableIPv6
	if len(n.labels) > 0 {
		to.labels = make(map[string]string, len(n.labels))
		for k, v := range n.labels {
			if _, ok := to.labels[k]; !ok {
				to.labels[k] = v
			}
		}
	}
	if len(n.ipamType) != 0 {
		to.ipamType = n.ipamType
	}
	if len(n.ipamOptions) > 0 {
		to.ipamOptions = make(map[string]string, len(n.ipamOptions))
		for k, v := range n.ipamOptions {
			if _, ok := to.ipamOptions[k]; !ok {
				to.ipamOptions[k] = v
			}
		}
	}
	if len(n.ipamV4Config) > 0 {
		to.ipamV4Config = make([]*IpamConf, 0, len(n.ipamV4Config))
		to.ipamV4Config = append(to.ipamV4Config, n.ipamV4Config...)
	}
	if len(n.ipamV6Config) > 0 {
		to.ipamV6Config = make([]*IpamConf, 0, len(n.ipamV6Config))
		to.ipamV6Config = append(to.ipamV6Config, n.ipamV6Config...)
	}
	if len(n.generic) > 0 {
		to.generic = options.Generic{}
		for k, v := range n.generic {
			to.generic[k] = v
		}
	}
	return nil
}

func (n *network) CopyTo(o datastore.KVObject) error {
	n.mu.Lock()
	defer n.mu.Unlock()

	dstN := o.(*network)
	dstN.name = n.name
	dstN.id = n.id
	dstN.created = n.created
	dstN.networkType = n.networkType
	dstN.scope = n.scope
	dstN.dynamic = n.dynamic
	dstN.ipamType = n.ipamType
	dstN.enableIPv6 = n.enableIPv6
	dstN.persist = n.persist
	dstN.postIPv6 = n.postIPv6
	dstN.dbIndex = n.dbIndex
	dstN.dbExists = n.dbExists
	dstN.drvOnce = n.drvOnce
	dstN.internal = n.internal
	dstN.attachable = n.attachable
	dstN.inDelete = n.inDelete
	dstN.ingress = n.ingress
	dstN.configOnly = n.configOnly
	dstN.configFrom = n.configFrom
	dstN.loadBalancerIP = n.loadBalancerIP
	dstN.loadBalancerMode = n.loadBalancerMode

	// copy labels
	if dstN.labels == nil {
		dstN.labels = make(map[string]string, len(n.labels))
	}
	for k, v := range n.labels {
		dstN.labels[k] = v
	}

	if n.ipamOptions != nil {
		dstN.ipamOptions = make(map[string]string, len(n.ipamOptions))
		for k, v := range n.ipamOptions {
			dstN.ipamOptions[k] = v
		}
	}

	for _, v4conf := range n.ipamV4Config {
		dstV4Conf := &IpamConf{}
		if err := v4conf.CopyTo(dstV4Conf); err != nil {
			return err
		}
		dstN.ipamV4Config = append(dstN.ipamV4Config, dstV4Conf)
	}

	for _, v4info := range n.ipamV4Info {
		dstV4Info := &IpamInfo{}
		if err := v4info.CopyTo(dstV4Info); err != nil {
			return err
		}
		dstN.ipamV4Info = append(dstN.ipamV4Info, dstV4Info)
	}

	for _, v6conf := range n.ipamV6Config {
		dstV6Conf := &IpamConf{}
		if err := v6conf.CopyTo(dstV6Conf); err != nil {
			return err
		}
		dstN.ipamV6Config = append(dstN.ipamV6Config, dstV6Conf)
	}

	for _, v6info := range n.ipamV6Info {
		dstV6Info := &IpamInfo{}
		if err := v6info.CopyTo(dstV6Info); err != nil {
			return err
		}
		dstN.ipamV6Info = append(dstN.ipamV6Info, dstV6Info)
	}

	dstN.generic = options.Generic{}
	for k, v := range n.generic {
		dstN.generic[k] = v
	}

	return nil
}

func (n *network) DataScope() string {
	s := n.Scope()
	// All swarm scope networks have local datascope
	if s == datastore.SwarmScope {
		s = datastore.LocalScope
	}
	return s
}

func (n *network) getEpCnt() *endpointCnt {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.epCnt
}

// TODO : Can be made much more generic with the help of reflection (but has some golang limitations)
func (n *network) MarshalJSON() ([]byte, error) {
	netMap := make(map[string]interface{})
	netMap["name"] = n.name
	netMap["id"] = n.id
	netMap["created"] = n.created
	netMap["networkType"] = n.networkType
	netMap["scope"] = n.scope
	netMap["labels"] = n.labels
	netMap["ipamType"] = n.ipamType
	netMap["ipamOptions"] = n.ipamOptions
	netMap["addrSpace"] = n.addrSpace
	netMap["enableIPv6"] = n.enableIPv6
	if n.generic != nil {
		netMap["generic"] = n.generic
	}
	netMap["persist"] = n.persist
	netMap["postIPv6"] = n.postIPv6
	if len(n.ipamV4Config) > 0 {
		ics, err := json.Marshal(n.ipamV4Config)
		if err != nil {
			return nil, err
		}
		netMap["ipamV4Config"] = string(ics)
	}
	if len(n.ipamV4Info) > 0 {
		iis, err := json.Marshal(n.ipamV4Info)
		if err != nil {
			return nil, err
		}
		netMap["ipamV4Info"] = string(iis)
	}
	if len(n.ipamV6Config) > 0 {
		ics, err := json.Marshal(n.ipamV6Config)
		if err != nil {
			return nil, err
		}
		netMap["ipamV6Config"] = string(ics)
	}
	if len(n.ipamV6Info) > 0 {
		iis, err := json.Marshal(n.ipamV6Info)
		if err != nil {
			return nil, err
		}
		netMap["ipamV6Info"] = string(iis)
	}
	netMap["internal"] = n.internal
	netMap["attachable"] = n.attachable
	netMap["inDelete"] = n.inDelete
	netMap["ingress"] = n.ingress
	netMap["configOnly"] = n.configOnly
	netMap["configFrom"] = n.configFrom
	netMap["loadBalancerIP"] = n.loadBalancerIP
	netMap["loadBalancerMode"] = n.loadBalancerMode
	return json.Marshal(netMap)
}

// TODO : Can be made much more generic with the help of reflection (but has some golang limitations)
func (n *network) UnmarshalJSON(b []byte) (err error) {
	var netMap map[string]interface{}
	if err := json.Unmarshal(b, &netMap); err != nil {
		return err
	}
	n.name = netMap["name"].(string)
	n.id = netMap["id"].(string)
	// "created" is not available in older versions
	if v, ok := netMap["created"]; ok {
		// n.created is time.Time but marshalled as string
		if err = n.created.UnmarshalText([]byte(v.(string))); err != nil {
			logrus.Warnf("failed to unmarshal creation time %v: %v", v, err)
			n.created = time.Time{}
		}
	}
	n.networkType = netMap["networkType"].(string)
	n.enableIPv6 = netMap["enableIPv6"].(bool)

	// if we weren't unmarshaling to netMap we could simply set n.labels
	// unfortunately, we can't because map[string]interface{} != map[string]string
	if labels, ok := netMap["labels"].(map[string]interface{}); ok {
		n.labels = make(map[string]string, len(labels))
		for label, value := range labels {
			n.labels[label] = value.(string)
		}
	}

	if v, ok := netMap["ipamOptions"]; ok {
		if iOpts, ok := v.(map[string]interface{}); ok {
			n.ipamOptions = make(map[string]string, len(iOpts))
			for k, v := range iOpts {
				n.ipamOptions[k] = v.(string)
			}
		}
	}

	if v, ok := netMap["generic"]; ok {
		n.generic = v.(map[string]interface{})
		// Restore opts in their map[string]string form
		if v, ok := n.generic[netlabel.GenericData]; ok {
			var lmap map[string]string
			ba, err := json.Marshal(v)
			if err != nil {
				return err
			}
			if err := json.Unmarshal(ba, &lmap); err != nil {
				return err
			}
			n.generic[netlabel.GenericData] = lmap
		}
	}
	if v, ok := netMap["persist"]; ok {
		n.persist = v.(bool)
	}
	if v, ok := netMap["postIPv6"]; ok {
		n.postIPv6 = v.(bool)
	}
	if v, ok := netMap["ipamType"]; ok {
		n.ipamType = v.(string)
	} else {
		n.ipamType = ipamapi.DefaultIPAM
	}
	if v, ok := netMap["addrSpace"]; ok {
		n.addrSpace = v.(string)
	}
	if v, ok := netMap["ipamV4Config"]; ok {
		if err := json.Unmarshal([]byte(v.(string)), &n.ipamV4Config); err != nil {
			return err
		}
	}
	if v, ok := netMap["ipamV4Info"]; ok {
		if err := json.Unmarshal([]byte(v.(string)), &n.ipamV4Info); err != nil {
			return err
		}
	}
	if v, ok := netMap["ipamV6Config"]; ok {
		if err := json.Unmarshal([]byte(v.(string)), &n.ipamV6Config); err != nil {
			return err
		}
	}
	if v, ok := netMap["ipamV6Info"]; ok {
		if err := json.Unmarshal([]byte(v.(string)), &n.ipamV6Info); err != nil {
			return err
		}
	}
	if v, ok := netMap["internal"]; ok {
		n.internal = v.(bool)
	}
	if v, ok := netMap["attachable"]; ok {
		n.attachable = v.(bool)
	}
	if s, ok := netMap["scope"]; ok {
		n.scope = s.(string)
	}
	if v, ok := netMap["inDelete"]; ok {
		n.inDelete = v.(bool)
	}
	if v, ok := netMap["ingress"]; ok {
		n.ingress = v.(bool)
	}
	if v, ok := netMap["configOnly"]; ok {
		n.configOnly = v.(bool)
	}
	if v, ok := netMap["configFrom"]; ok {
		n.configFrom = v.(string)
	}
	if v, ok := netMap["loadBalancerIP"]; ok {
		n.loadBalancerIP = net.ParseIP(v.(string))
	}
	n.loadBalancerMode = loadBalancerModeDefault
	if v, ok := netMap["loadBalancerMode"]; ok {
		n.loadBalancerMode = v.(string)
	}
	// Reconcile old networks with the recently added `--ipv6` flag
	if !n.enableIPv6 {
		n.enableIPv6 = len(n.ipamV6Info) > 0
	}
	return nil
}

// NetworkOption is an option setter function type used to pass various options to
// NewNetwork method. The various setter functions of type NetworkOption are
// provided by libnetwork, they look like NetworkOptionXXXX(...)
type NetworkOption func(n *network)

// NetworkOptionGeneric function returns an option setter for a Generic option defined
// in a Dictionary of Key-Value pair
func NetworkOptionGeneric(generic map[string]interface{}) NetworkOption {
	return func(n *network) {
		if n.generic == nil {
			n.generic = make(map[string]interface{})
		}
		if val, ok := generic[netlabel.EnableIPv6]; ok {
			n.enableIPv6 = val.(bool)
		}
		if val, ok := generic[netlabel.Internal]; ok {
			n.internal = val.(bool)
		}
		for k, v := range generic {
			n.generic[k] = v
		}
	}
}

// NetworkOptionIngress returns an option setter to indicate if a network is
// an ingress network.
func NetworkOptionIngress(ingress bool) NetworkOption {
	return func(n *network) {
		n.ingress = ingress
	}
}

// NetworkOptionPersist returns an option setter to set persistence policy for a network
func NetworkOptionPersist(persist bool) NetworkOption {
	return func(n *network) {
		n.persist = persist
	}
}

// NetworkOptionEnableIPv6 returns an option setter to explicitly configure IPv6
func NetworkOptionEnableIPv6(enableIPv6 bool) NetworkOption {
	return func(n *network) {
		if n.generic == nil {
			n.generic = make(map[string]interface{})
		}
		n.enableIPv6 = enableIPv6
		n.generic[netlabel.EnableIPv6] = enableIPv6
	}
}

// NetworkOptionInternalNetwork returns an option setter to config the network
// to be internal which disables default gateway service
func NetworkOptionInternalNetwork() NetworkOption {
	return func(n *network) {
		if n.generic == nil {
			n.generic = make(map[string]interface{})
		}
		n.internal = true
		n.generic[netlabel.Internal] = true
	}
}

// NetworkOptionAttachable returns an option setter to set attachable for a network
func NetworkOptionAttachable(attachable bool) NetworkOption {
	return func(n *network) {
		n.attachable = attachable
	}
}

// NetworkOptionScope returns an option setter to overwrite the network's scope.
// By default the network's scope is set to the network driver's datascope.
func NetworkOptionScope(scope string) NetworkOption {
	return func(n *network) {
		n.scope = scope
	}
}

// NetworkOptionIpam function returns an option setter for the ipam configuration for this network
func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf, opts map[string]string) NetworkOption {
	return func(n *network) {
		if ipamDriver != "" {
			n.ipamType = ipamDriver
			if ipamDriver == ipamapi.DefaultIPAM {
				n.ipamType = defaultIpamForNetworkType(n.Type())
			}
		}
		n.ipamOptions = opts
		n.addrSpace = addrSpace
		n.ipamV4Config = ipV4
		n.ipamV6Config = ipV6
	}
}

// NetworkOptionLBEndpoint function returns an option setter for the configuration of the load balancer endpoint for this network
func NetworkOptionLBEndpoint(ip net.IP) NetworkOption {
	return func(n *network) {
		n.loadBalancerIP = ip
	}
}

// NetworkOptionDriverOpts function returns an option setter for any driver parameter described by a map
func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
	return func(n *network) {
		if n.generic == nil {
			n.generic = make(map[string]interface{})
		}
		if opts == nil {
			opts = make(map[string]string)
		}
		// Store the options
		n.generic[netlabel.GenericData] = opts
	}
}

// NetworkOptionLabels function returns an option setter for labels specific to a network
func NetworkOptionLabels(labels map[string]string) NetworkOption {
	return func(n *network) {
		n.labels = labels
	}
}

// NetworkOptionDynamic function returns an option setter for dynamic option for a network
func NetworkOptionDynamic() NetworkOption {
	return func(n *network) {
		n.dynamic = true
	}
}

// NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
// It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
// to a container as combination of fixed-cidr-v6 + mac-address
// TODO: Remove this option setter once we support endpoint ipam options
func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
	return func(n *network) {
		n.postIPv6 = enable
	}
}

// NetworkOptionConfigOnly tells controller this network is
// a configuration only network. It serves as a configuration
// for other networks.
func NetworkOptionConfigOnly() NetworkOption {
	return func(n *network) {
		n.configOnly = true
	}
}

// NetworkOptionConfigFrom tells controller to pick the
// network configuration from a configuration only network
func NetworkOptionConfigFrom(name string) NetworkOption {
	return func(n *network) {
		n.configFrom = name
	}
}

func (n *network) processOptions(options ...NetworkOption) {
	for _, opt := range options {
		if opt != nil {
			opt(n)
		}
	}
}

type networkDeleteParams struct {
	rmLBEndpoint bool
}

// NetworkDeleteOption is a type for optional parameters to pass to the
// network.Delete() function.
type NetworkDeleteOption func(p *networkDeleteParams)

// NetworkDeleteOptionRemoveLB informs a network.Delete() operation that should
// remove the load balancer endpoint for this network.  Note that the Delete()
// method will automatically remove a load balancing endpoint for most networks
// when the network is otherwise empty.  However, this does not occur for some
// networks.  In particular, networks marked as ingress (which are supposed to
// be more permanent than other overlay networks) won't automatically remove
// the LB endpoint on Delete().  This method allows for explicit removal of
// such networks provided there are no other endpoints present in the network.
// If the network still has non-LB endpoints present, Delete() will not
// remove the LB endpoint and will return an error.
func NetworkDeleteOptionRemoveLB(p *networkDeleteParams) {
	p.rmLBEndpoint = true
}

func (n *network) resolveDriver(name string, load bool) (driverapi.Driver, driverapi.Capability, error) {
	c := n.getController()

	// Check if a driver for the specified network type is available
	d, cap := c.drvRegistry.Driver(name)
	if d == nil {
		if load {
			err := c.loadDriver(name)
			if err != nil {
				return nil, driverapi.Capability{}, err
			}

			d, cap = c.drvRegistry.Driver(name)
			if d == nil {
				return nil, driverapi.Capability{}, fmt.Errorf("could not resolve driver %s in registry", name)
			}
		} else {
			// don't fail if driver loading is not required
			return nil, driverapi.Capability{}, nil
		}
	}

	return d, cap, nil
}

func (n *network) driverIsMultihost() bool {
	_, cap, err := n.resolveDriver(n.networkType, true)
	if err != nil {
		return false
	}
	return cap.ConnectivityScope == datastore.GlobalScope
}

func (n *network) driver(load bool) (driverapi.Driver, error) {
	d, cap, err := n.resolveDriver(n.networkType, load)
	if err != nil {
		return nil, err
	}

	n.mu.Lock()
	// If load is not required, driver, cap and err may all be nil
	if n.scope == "" {
		n.scope = cap.DataScope
	}
	if n.dynamic {
		// If the network is dynamic, then it is swarm
		// scoped regardless of the backing driver.
		n.scope = datastore.SwarmScope
	}
	n.mu.Unlock()
	return d, nil
}

func (n *network) Delete(options ...NetworkDeleteOption) error {
	var params networkDeleteParams
	for _, opt := range options {
		opt(&params)
	}
	return n.delete(false, params.rmLBEndpoint)
}

// This function gets called in 3 ways:
//   - Delete() -- (false, false)
//     remove if endpoint count == 0 or endpoint count == 1 and
//     there is a load balancer IP
//   - Delete(libnetwork.NetworkDeleteOptionRemoveLB) -- (false, true)
//     remove load balancer and network if endpoint count == 1
//   - controller.networkCleanup() -- (true, true)
//     remove the network no matter what
func (n *network) delete(force bool, rmLBEndpoint bool) error {
	n.mu.Lock()
	c := n.ctrlr
	name := n.name
	id := n.id
	n.mu.Unlock()

	c.networkLocker.Lock(id)
	defer c.networkLocker.Unlock(id) //nolint:errcheck

	n, err := c.getNetworkFromStore(id)
	if err != nil {
		return &UnknownNetworkError{name: name, id: id}
	}

	// Only remove ingress on force removal or explicit LB endpoint removal
	if n.ingress && !force && !rmLBEndpoint {
		return &ActiveEndpointsError{name: n.name, id: n.id}
	}

	// Check that the network is empty
	var emptyCount uint64
	if n.hasLoadBalancerEndpoint() {
		emptyCount = 1
	}
	if !force && n.getEpCnt().EndpointCnt() > emptyCount {
		if n.configOnly {
			return types.ForbiddenErrorf("configuration network %q is in use", n.Name())
		}
		return &ActiveEndpointsError{name: n.name, id: n.id}
	}

	if n.hasLoadBalancerEndpoint() {
		// If we got to this point, then the following must hold:
		//  * force is true OR endpoint count == 1
		if err := n.deleteLoadBalancerSandbox(); err != nil {
			if !force {
				return err
			}
			// continue deletion when force is true even on error
			logrus.Warnf("Error deleting load balancer sandbox: %v", err)
		}
		// Reload the network from the store to update the epcnt.
		n, err = c.getNetworkFromStore(id)
		if err != nil {
			return &UnknownNetworkError{name: name, id: id}
		}
	}

	// Up to this point, errors that we returned were recoverable.
	// From here on, any errors leave us in an inconsistent state.
	// This is unfortunate, but there isn't a safe way to
	// reconstitute a load-balancer endpoint after removing it.

	// Mark the network for deletion
	n.inDelete = true
	if err = c.updateToStore(n); err != nil {
		return fmt.Errorf("error marking network %s (%s) for deletion: %v", n.Name(), n.ID(), err)
	}

	if n.ConfigFrom() != "" {
		if t, err := c.getConfigNetwork(n.ConfigFrom()); err == nil {
			if err := t.getEpCnt().DecEndpointCnt(); err != nil {
				logrus.Warnf("Failed to update reference count for configuration network %q on removal of network %q: %v",
					t.Name(), n.Name(), err)
			}
		} else {
			logrus.Warnf("Could not find configuration network %q during removal of network %q", n.configFrom, n.Name())
		}
	}

	if n.configOnly {
		goto removeFromStore
	}

	n.ipamRelease()
	if err = c.updateToStore(n); err != nil {
		logrus.Warnf("Failed to update store after ipam release for network %s (%s): %v", n.Name(), n.ID(), err)
	}

	// We are about to delete the network. Leave the gossip
	// cluster for the network to stop all incoming network
	// specific gossip updates before cleaning up all the service
	// bindings for the network. But cleanup service binding
	// before deleting the network from the store since service
	// bindings cleanup requires the network in the store.
	n.cancelDriverWatches()
	if err = n.leaveCluster(); err != nil {
		logrus.Errorf("Failed leaving network %s from the agent cluster: %v", n.Name(), err)
	}

	// Cleanup the service discovery for this network
	c.cleanupServiceDiscovery(n.ID())

	// Cleanup the load balancer. On Windows this call is required
	// to remove remote loadbalancers in VFP, and must be performed before
	// dataplane network deletion.
	if runtime.GOOS == "windows" {
		c.cleanupServiceBindings(n.ID())
	}

	// Delete the network from the dataplane
	if err = n.deleteNetwork(); err != nil {
		if !force {
			return err
		}
		logrus.Debugf("driver failed to delete stale network %s (%s): %v", n.Name(), n.ID(), err)
	}

removeFromStore:
	// deleteFromStore performs an atomic delete operation and the
	// network.epCnt will help prevent any possible
	// race between endpoint join and network delete
	if err = c.deleteFromStore(n.getEpCnt()); err != nil {
		if !force {
			return fmt.Errorf("error deleting network endpoint count from store: %v", err)
		}
		logrus.Debugf("Error deleting endpoint count from store for stale network %s (%s) for deletion: %v", n.Name(), n.ID(), err)
	}

	if err = c.deleteFromStore(n); err != nil {
		return fmt.Errorf("error deleting network from store: %v", err)
	}

	return nil
}

func (n *network) deleteNetwork() error {
	d, err := n.driver(true)
	if err != nil {
		return fmt.Errorf("failed deleting network: %v", err)
	}

	if err := d.DeleteNetwork(n.ID()); err != nil {
		// Forbidden Errors should be honored
		if _, ok := err.(types.ForbiddenError); ok {
			return err
		}

		if _, ok := err.(types.MaskableError); !ok {
			logrus.Warnf("driver error deleting network %s : %v", n.name, err)
		}
	}

	for _, resolver := range n.resolver {
		resolver.Stop()
	}
	return nil
}

func (n *network) addEndpoint(ep *Endpoint) error {
	d, err := n.driver(true)
	if err != nil {
		return fmt.Errorf("failed to add endpoint: %v", err)
	}

	err = d.CreateEndpoint(n.id, ep.id, ep.Interface(), ep.generic)
	if err != nil {
		return types.InternalErrorf("failed to create endpoint %s on network %s: %v",
			ep.Name(), n.Name(), err)
	}

	return nil
}

func (n *network) CreateEndpoint(name string, options ...EndpointOption) (*Endpoint, error) {
	var err error
	if !config.IsValidName(name) {
		return nil, ErrInvalidName(name)
	}

	if n.ConfigOnly() {
		return nil, types.ForbiddenErrorf("cannot create endpoint on configuration-only network")
	}

	if _, err = n.EndpointByName(name); err == nil {
		return nil, types.ForbiddenErrorf("endpoint with name %s already exists in network %s", name, n.Name())
	}

	n.ctrlr.networkLocker.Lock(n.id)
	defer n.ctrlr.networkLocker.Unlock(n.id) //nolint:errcheck

	return n.createEndpoint(name, options...)
}

func (n *network) createEndpoint(name string, options ...EndpointOption) (*Endpoint, error) {
	var err error

	ep := &Endpoint{name: name, generic: make(map[string]interface{}), iface: &endpointInterface{}}
	ep.id = stringid.GenerateRandomID()

	// Initialize ep.network with a possibly stale copy of n. We need this to get network from
	// store. But once we get it from store we will have the most uptodate copy possibly.
	ep.network = n
	ep.network, err = ep.getNetworkFromStore()
	if err != nil {
		logrus.Errorf("failed to get network during CreateEndpoint: %v", err)
		return nil, err
	}
	n = ep.network

	ep.processOptions(options...)

	for _, llIPNet := range ep.Iface().LinkLocalAddresses() {
		if !llIPNet.IP.IsLinkLocalUnicast() {
			return nil, types.BadRequestErrorf("invalid link local IP address: %v", llIPNet.IP)
		}
	}

	if opt, ok := ep.generic[netlabel.MacAddress]; ok {
		if mac, ok := opt.(net.HardwareAddr); ok {
			ep.iface.mac = mac
		}
	}

	ipam, capability, err := n.getController().getIPAMDriver(n.ipamType)
	if err != nil {
		return nil, err
	}

	if capability.RequiresMACAddress {
		if ep.iface.mac == nil {
			ep.iface.mac = netutils.GenerateRandomMAC()
		}
		if ep.ipamOptions == nil {
			ep.ipamOptions = make(map[string]string)
		}
		ep.ipamOptions[netlabel.MacAddress] = ep.iface.mac.String()
	}

	if err = ep.assignAddress(ipam, true, n.enableIPv6 && !n.postIPv6); err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			ep.releaseAddress()
		}
	}()

	if err = n.addEndpoint(ep); err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			if e := ep.deleteEndpoint(false); e != nil {
				logrus.Warnf("cleaning up endpoint failed %s : %v", name, e)
			}
		}
	}()

	// We should perform updateToStore call right after addEndpoint
	// in order to have iface properly configured
	if err = n.getController().updateToStore(ep); err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			if e := n.getController().deleteFromStore(ep); e != nil {
				logrus.Warnf("error rolling back endpoint %s from store: %v", name, e)
			}
		}
	}()

	if err = ep.assignAddress(ipam, false, n.enableIPv6 && n.postIPv6); err != nil {
		return nil, err
	}

	// Watch for service records
	n.getController().watchSvcRecord(ep)
	defer func() {
		if err != nil {
			n.getController().unWatchSvcRecord(ep)
		}
	}()

	// Increment endpoint count to indicate completion of endpoint addition
	if err = n.getEpCnt().IncEndpointCnt(); err != nil {
		return nil, err
	}

	return ep, nil
}

func (n *network) Endpoints() []*Endpoint {
	endpoints, err := n.getEndpointsFromStore()
	if err != nil {
		logrus.Error(err)
	}
	return endpoints
}

func (n *network) WalkEndpoints(walker EndpointWalker) {
	for _, e := range n.Endpoints() {
		if walker(e) {
			return
		}
	}
}

func (n *network) EndpointByName(name string) (*Endpoint, error) {
	if name == "" {
		return nil, ErrInvalidName(name)
	}
	var e *Endpoint

	s := func(current *Endpoint) bool {
		if current.Name() == name {
			e = current
			return true
		}
		return false
	}

	n.WalkEndpoints(s)

	if e == nil {
		return nil, ErrNoSuchEndpoint(name)
	}

	return e, nil
}

func (n *network) EndpointByID(id string) (*Endpoint, error) {
	if id == "" {
		return nil, ErrInvalidID(id)
	}

	ep, err := n.getEndpointFromStore(id)
	if err != nil {
		return nil, ErrNoSuchEndpoint(id)
	}

	return ep, nil
}

func (n *network) updateSvcRecord(ep *Endpoint, localEps []*Endpoint, isAdd bool) {
	var ipv6 net.IP
	epName := ep.Name()
	if iface := ep.Iface(); iface != nil && iface.Address() != nil {
		myAliases := ep.MyAliases()
		if iface.AddressIPv6() != nil {
			ipv6 = iface.AddressIPv6().IP
		}

		serviceID := ep.svcID
		if serviceID == "" {
			serviceID = ep.ID()
		}
		if isAdd {
			// If anonymous endpoint has an alias use the first alias
			// for ip->name mapping. Not having the reverse mapping
			// breaks some apps
			if ep.isAnonymous() {
				if len(myAliases) > 0 {
					n.addSvcRecords(ep.ID(), myAliases[0], serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
				}
			} else {
				n.addSvcRecords(ep.ID(), epName, serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
			}
			for _, alias := range myAliases {
				n.addSvcRecords(ep.ID(), alias, serviceID, iface.Address().IP, ipv6, false, "updateSvcRecord")
			}
		} else {
			if ep.isAnonymous() {
				if len(myAliases) > 0 {
					n.deleteSvcRecords(ep.ID(), myAliases[0], serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
				}
			} else {
				n.deleteSvcRecords(ep.ID(), epName, serviceID, iface.Address().IP, ipv6, true, "updateSvcRecord")
			}
			for _, alias := range myAliases {
				n.deleteSvcRecords(ep.ID(), alias, serviceID, iface.Address().IP, ipv6, false, "updateSvcRecord")
			}
		}
	}
}

func addIPToName(ipMap *setmatrix.SetMatrix[ipInfo], name, serviceID string, ip net.IP) {
	reverseIP := netutils.ReverseIP(ip.String())
	ipMap.Insert(reverseIP, ipInfo{
		name:      name,
		serviceID: serviceID,
	})
}

func delIPToName(ipMap *setmatrix.SetMatrix[ipInfo], name, serviceID string, ip net.IP) {
	reverseIP := netutils.ReverseIP(ip.String())
	ipMap.Remove(reverseIP, ipInfo{
		name:      name,
		serviceID: serviceID,
	})
}

func addNameToIP(svcMap *setmatrix.SetMatrix[svcMapEntry], name, serviceID string, epIP net.IP) {
	// Since DNS name resolution is case-insensitive, Use the lower-case form
	// of the name as the key into svcMap
	lowerCaseName := strings.ToLower(name)
	svcMap.Insert(lowerCaseName, svcMapEntry{
		ip:        epIP.String(),
		serviceID: serviceID,
	})
}

func delNameToIP(svcMap *setmatrix.SetMatrix[svcMapEntry], name, serviceID string, epIP net.IP) {
	lowerCaseName := strings.ToLower(name)
	svcMap.Remove(lowerCaseName, svcMapEntry{
		ip:        epIP.String(),
		serviceID: serviceID,
	})
}

func (n *network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP, ipMapUpdate bool, method string) {
	// Do not add service names for ingress network as this is a
	// routing only network
	if n.ingress {
		return
	}
	networkID := n.ID()
	logrus.Debugf("%s (%.7s).addSvcRecords(%s, %s, %s, %t) %s sid:%s", eID, networkID, name, epIP, epIPv6, ipMapUpdate, method, serviceID)

	c := n.getController()
	c.mu.Lock()
	defer c.mu.Unlock()

	sr, ok := c.svcRecords[networkID]
	if !ok {
		sr = &svcInfo{}
		c.svcRecords[networkID] = sr
	}

	if ipMapUpdate {
		addIPToName(&sr.ipMap, name, serviceID, epIP)
		if epIPv6 != nil {
			addIPToName(&sr.ipMap, name, serviceID, epIPv6)
		}
	}

	addNameToIP(&sr.svcMap, name, serviceID, epIP)
	if epIPv6 != nil {
		addNameToIP(&sr.svcIPv6Map, name, serviceID, epIPv6)
	}
}

func (n *network) deleteSvcRecords(eID, name, serviceID string, epIP net.IP, epIPv6 net.IP, ipMapUpdate bool, method string) {
	// Do not delete service names from ingress network as this is a
	// routing only network
	if n.ingress {
		return
	}
	networkID := n.ID()
	logrus.Debugf("%s (%.7s).deleteSvcRecords(%s, %s, %s, %t) %s sid:%s ", eID, networkID, name, epIP, epIPv6, ipMapUpdate, method, serviceID)

	c := n.getController()
	c.mu.Lock()
	defer c.mu.Unlock()

	sr, ok := c.svcRecords[networkID]
	if !ok {
		return
	}

	if ipMapUpdate {
		delIPToName(&sr.ipMap, name, serviceID, epIP)

		if epIPv6 != nil {
			delIPToName(&sr.ipMap, name, serviceID, epIPv6)
		}
	}

	delNameToIP(&sr.svcMap, name, serviceID, epIP)

	if epIPv6 != nil {
		delNameToIP(&sr.svcIPv6Map, name, serviceID, epIPv6)
	}
}

func (n *network) getSvcRecords(ep *Endpoint) []etchosts.Record {
	n.mu.Lock()
	defer n.mu.Unlock()

	if ep == nil {
		return nil
	}

	var recs []etchosts.Record

	epName := ep.Name()

	n.ctrlr.mu.Lock()
	defer n.ctrlr.mu.Unlock()
	sr, ok := n.ctrlr.svcRecords[n.id]
	if !ok {
		return nil
	}

	svcMapKeys := sr.svcMap.Keys()
	// Loop on service names on this network
	for _, k := range svcMapKeys {
		if strings.Split(k, ".")[0] == epName {
			continue
		}
		// Get all the IPs associated to this service
		mapEntryList, ok := sr.svcMap.Get(k)
		if !ok {
			// The key got deleted
			continue
		}
		if len(mapEntryList) == 0 {
			logrus.Warnf("Found empty list of IP addresses for service %s on network %s (%s)", k, n.name, n.id)
			continue
		}

		recs = append(recs, etchosts.Record{
			Hosts: k,
			IP:    mapEntryList[0].ip,
		})
	}

	return recs
}

func (n *network) getController() *Controller {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.ctrlr
}

func (n *network) ipamAllocate() error {
	if n.hasSpecialDriver() {
		return nil
	}

	ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
	if err != nil {
		return err
	}

	if n.addrSpace == "" {
		if n.addrSpace, err = n.deriveAddressSpace(); err != nil {
			return err
		}
	}

	err = n.ipamAllocateVersion(4, ipam)
	if err != nil {
		return err
	}

	defer func() {
		if err != nil {
			n.ipamReleaseVersion(4, ipam)
		}
	}()

	if !n.enableIPv6 {
		return nil
	}

	err = n.ipamAllocateVersion(6, ipam)
	return err
}

func (n *network) requestPoolHelper(ipam ipamapi.Ipam, addressSpace, preferredPool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
	for {
		poolID, pool, meta, err := ipam.RequestPool(addressSpace, preferredPool, subPool, options, v6)
		if err != nil {
			return "", nil, nil, err
		}

		// If the network belongs to global scope or the pool was
		// explicitly chosen or it is invalid, do not perform the overlap check.
		if n.Scope() == datastore.GlobalScope || preferredPool != "" || !types.IsIPNetValid(pool) {
			return poolID, pool, meta, nil
		}

		// Check for overlap and if none found, we have found the right pool.
		if _, err := netutils.FindAvailableNetwork([]*net.IPNet{pool}); err == nil {
			return poolID, pool, meta, nil
		}

		// Pool obtained in this iteration is
		// overlapping. Hold onto the pool and don't release
		// it yet, because we don't want ipam to give us back
		// the same pool over again. But make sure we still do
		// a deferred release when we have either obtained a
		// non-overlapping pool or ran out of pre-defined
		// pools.
		defer func() {
			if err := ipam.ReleasePool(poolID); err != nil {
				logrus.Warnf("Failed to release overlapping pool %s while returning from pool request helper for network %s", pool, n.Name())
			}
		}()

		// If this is a preferred pool request and the network
		// is local scope and there is an overlap, we fail the
		// network creation right here. The pool will be
		// released in the defer.
		if preferredPool != "" {
			return "", nil, nil, fmt.Errorf("requested subnet %s overlaps in the host", preferredPool)
		}
	}
}

func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
	var (
		cfgList  *[]*IpamConf
		infoList *[]*IpamInfo
		err      error
	)

	switch ipVer {
	case 4:
		cfgList = &n.ipamV4Config
		infoList = &n.ipamV4Info
	case 6:
		cfgList = &n.ipamV6Config
		infoList = &n.ipamV6Info
	default:
		return types.InternalErrorf("incorrect ip version passed to ipam allocate: %d", ipVer)
	}

	if len(*cfgList) == 0 {
		*cfgList = []*IpamConf{{}}
	}

	*infoList = make([]*IpamInfo, len(*cfgList))

	logrus.Debugf("Allocating IPv%d pools for network %s (%s)", ipVer, n.Name(), n.ID())

	for i, cfg := range *cfgList {
		if err = cfg.Validate(); err != nil {
			return err
		}
		d := &IpamInfo{}
		(*infoList)[i] = d

		d.AddressSpace = n.addrSpace
		d.PoolID, d.Pool, d.Meta, err = n.requestPoolHelper(ipam, n.addrSpace, cfg.PreferredPool, cfg.SubPool, n.ipamOptions, ipVer == 6)
		if err != nil {
			return err
		}

		defer func() {
			if err != nil {
				if err := ipam.ReleasePool(d.PoolID); err != nil {
					logrus.Warnf("Failed to release address pool %s after failure to create network %s (%s)", d.PoolID, n.Name(), n.ID())
				}
			}
		}()

		if gws, ok := d.Meta[netlabel.Gateway]; ok {
			if d.Gateway, err = types.ParseCIDR(gws); err != nil {
				return types.BadRequestErrorf("failed to parse gateway address (%v) returned by ipam driver: %v", gws, err)
			}
		}

		// If user requested a specific gateway, libnetwork will allocate it
		// irrespective of whether ipam driver returned a gateway already.
		// If none of the above is true, libnetwork will allocate one.
		if cfg.Gateway != "" || d.Gateway == nil {
			var gatewayOpts = map[string]string{
				ipamapi.RequestAddressType: netlabel.Gateway,
			}
			if d.Gateway, _, err = ipam.RequestAddress(d.PoolID, net.ParseIP(cfg.Gateway), gatewayOpts); err != nil {
				return types.InternalErrorf("failed to allocate gateway (%v): %v", cfg.Gateway, err)
			}
		}

		// Auxiliary addresses must be part of the master address pool
		// If they fall into the container addressable pool, libnetwork will reserve them
		if cfg.AuxAddresses != nil {
			var ip net.IP
			d.IPAMData.AuxAddresses = make(map[string]*net.IPNet, len(cfg.AuxAddresses))
			for k, v := range cfg.AuxAddresses {
				if ip = net.ParseIP(v); ip == nil {
					return types.BadRequestErrorf("non parsable secondary ip address (%s:%s) passed for network %s", k, v, n.Name())
				}
				if !d.Pool.Contains(ip) {
					return types.ForbiddenErrorf("auxiliary address: (%s:%s) must belong to the master pool: %s", k, v, d.Pool)
				}
				// Attempt reservation in the container addressable pool, silent the error if address does not belong to that pool
				if d.IPAMData.AuxAddresses[k], _, err = ipam.RequestAddress(d.PoolID, ip, nil); err != nil && err != ipamapi.ErrIPOutOfRange {
					return types.InternalErrorf("failed to allocate secondary ip address (%s:%s): %v", k, v, err)
				}
			}
		}
	}

	return nil
}

func (n *network) ipamRelease() {
	if n.hasSpecialDriver() {
		return
	}
	ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
	if err != nil {
		logrus.Warnf("Failed to retrieve ipam driver to release address pool(s) on delete of network %s (%s): %v", n.Name(), n.ID(), err)
		return
	}
	n.ipamReleaseVersion(4, ipam)
	n.ipamReleaseVersion(6, ipam)
}

func (n *network) ipamReleaseVersion(ipVer int, ipam ipamapi.Ipam) {
	var infoList *[]*IpamInfo

	switch ipVer {
	case 4:
		infoList = &n.ipamV4Info
	case 6:
		infoList = &n.ipamV6Info
	default:
		logrus.Warnf("incorrect ip version passed to ipam release: %d", ipVer)
		return
	}

	if len(*infoList) == 0 {
		return
	}

	logrus.Debugf("releasing IPv%d pools from network %s (%s)", ipVer, n.Name(), n.ID())

	for _, d := range *infoList {
		if d.Gateway != nil {
			if err := ipam.ReleaseAddress(d.PoolID, d.Gateway.IP); err != nil {
				logrus.Warnf("Failed to release gateway ip address %s on delete of network %s (%s): %v", d.Gateway.IP, n.Name(), n.ID(), err)
			}
		}
		if d.IPAMData.AuxAddresses != nil {
			for k, nw := range d.IPAMData.AuxAddresses {
				if d.Pool.Contains(nw.IP) {
					if err := ipam.ReleaseAddress(d.PoolID, nw.IP); err != nil && err != ipamapi.ErrIPOutOfRange {
						logrus.Warnf("Failed to release secondary ip address %s (%v) on delete of network %s (%s): %v", k, nw.IP, n.Name(), n.ID(), err)
					}
				}
			}
		}
		if err := ipam.ReleasePool(d.PoolID); err != nil {
			logrus.Warnf("Failed to release address pool %s on delete of network %s (%s): %v", d.PoolID, n.Name(), n.ID(), err)
		}
	}

	*infoList = nil
}

func (n *network) getIPInfo(ipVer int) []*IpamInfo {
	var info []*IpamInfo
	switch ipVer {
	case 4:
		info = n.ipamV4Info
	case 6:
		info = n.ipamV6Info
	default:
		return nil
	}
	l := make([]*IpamInfo, 0, len(info))
	n.mu.Lock()
	l = append(l, info...)
	n.mu.Unlock()
	return l
}

func (n *network) getIPData(ipVer int) []driverapi.IPAMData {
	var info []*IpamInfo
	switch ipVer {
	case 4:
		info = n.ipamV4Info
	case 6:
		info = n.ipamV6Info
	default:
		return nil
	}
	l := make([]driverapi.IPAMData, 0, len(info))
	n.mu.Lock()
	for _, d := range info {
		l = append(l, d.IPAMData)
	}
	n.mu.Unlock()
	return l
}

func (n *network) deriveAddressSpace() (string, error) {
	ipam, _ := n.getController().ipamRegistry.IPAM(n.ipamType)
	if ipam == nil {
		return "", types.NotFoundErrorf("failed to get default address space: unknown ipam type %q", n.ipamType)
	}
	local, global, err := ipam.GetDefaultAddressSpaces()
	if err != nil {
		return "", types.NotFoundErrorf("failed to get default address space: %v", err)
	}
	if n.DataScope() == datastore.GlobalScope {
		return global, nil
	}
	return local, nil
}

func (n *network) Info() NetworkInfo {
	return n
}

func (n *network) Peers() []networkdb.PeerInfo {
	if !n.Dynamic() {
		return []networkdb.PeerInfo{}
	}

	agent := n.getController().getAgent()
	if agent == nil {
		return []networkdb.PeerInfo{}
	}

	return agent.networkDB.Peers(n.ID())
}

func (n *network) DriverOptions() map[string]string {
	n.mu.Lock()
	defer n.mu.Unlock()
	if n.generic != nil {
		if m, ok := n.generic[netlabel.GenericData]; ok {
			return m.(map[string]string)
		}
	}
	return map[string]string{}
}

func (n *network) Scope() string {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.scope
}

func (n *network) IpamConfig() (string, map[string]string, []*IpamConf, []*IpamConf) {
	n.mu.Lock()
	defer n.mu.Unlock()

	v4L := make([]*IpamConf, len(n.ipamV4Config))
	v6L := make([]*IpamConf, len(n.ipamV6Config))

	for i, c := range n.ipamV4Config {
		cc := &IpamConf{}
		if err := c.CopyTo(cc); err != nil {
			logrus.WithError(err).Error("Error copying ipam ipv4 config")
		}
		v4L[i] = cc
	}

	for i, c := range n.ipamV6Config {
		cc := &IpamConf{}
		if err := c.CopyTo(cc); err != nil {
			logrus.WithError(err).Debug("Error copying ipam ipv6 config")
		}
		v6L[i] = cc
	}

	return n.ipamType, n.ipamOptions, v4L, v6L
}

func (n *network) IpamInfo() ([]*IpamInfo, []*IpamInfo) {
	n.mu.Lock()
	defer n.mu.Unlock()

	v4Info := make([]*IpamInfo, len(n.ipamV4Info))
	v6Info := make([]*IpamInfo, len(n.ipamV6Info))

	for i, info := range n.ipamV4Info {
		ic := &IpamInfo{}
		if err := info.CopyTo(ic); err != nil {
			logrus.WithError(err).Error("Error copying ipv4 ipam config")
		}
		v4Info[i] = ic
	}

	for i, info := range n.ipamV6Info {
		ic := &IpamInfo{}
		if err := info.CopyTo(ic); err != nil {
			logrus.WithError(err).Error("Error copying ipv6 ipam config")
		}
		v6Info[i] = ic
	}

	return v4Info, v6Info
}

func (n *network) Internal() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.internal
}

func (n *network) Attachable() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.attachable
}

func (n *network) Ingress() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.ingress
}

func (n *network) Dynamic() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.dynamic
}

func (n *network) IPv6Enabled() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.enableIPv6
}

func (n *network) ConfigFrom() string {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.configFrom
}

func (n *network) ConfigOnly() bool {
	n.mu.Lock()
	defer n.mu.Unlock()

	return n.configOnly
}

func (n *network) Labels() map[string]string {
	n.mu.Lock()
	defer n.mu.Unlock()

	var lbls = make(map[string]string, len(n.labels))
	for k, v := range n.labels {
		lbls[k] = v
	}

	return lbls
}

func (n *network) TableEventRegister(tableName string, objType driverapi.ObjectType) error {
	if !driverapi.IsValidType(objType) {
		return fmt.Errorf("invalid object type %v in registering table, %s", objType, tableName)
	}

	t := networkDBTable{
		name:    tableName,
		objType: objType,
	}
	n.mu.Lock()
	defer n.mu.Unlock()
	n.driverTables = append(n.driverTables, t)
	return nil
}

func (n *network) UpdateIpamConfig(ipV4Data []driverapi.IPAMData) {
	ipamV4Config := make([]*IpamConf, len(ipV4Data))

	for i, data := range ipV4Data {
		ic := &IpamConf{}
		ic.PreferredPool = data.Pool.String()
		ic.Gateway = data.Gateway.IP.String()
		ipamV4Config[i] = ic
	}

	n.mu.Lock()
	defer n.mu.Unlock()
	n.ipamV4Config = ipamV4Config
}

// Special drivers are ones which do not need to perform any network plumbing
func (n *network) hasSpecialDriver() bool {
	return n.Type() == "host" || n.Type() == "null"
}

func (n *network) hasLoadBalancerEndpoint() bool {
	return len(n.loadBalancerIP) != 0
}

func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) {
	var ipv6Miss bool

	c := n.getController()
	networkID := n.ID()
	c.mu.Lock()
	defer c.mu.Unlock()
	sr, ok := c.svcRecords[networkID]

	if !ok {
		return nil, false
	}

	req = strings.TrimSuffix(req, ".")
	req = strings.ToLower(req)
	ipSet, ok := sr.svcMap.Get(req)

	if ipType == types.IPv6 {
		// If the name resolved to v4 address then its a valid name in
		// the docker network domain. If the network is not v6 enabled
		// set ipv6Miss to filter the DNS query from going to external
		// resolvers.
		if ok && !n.enableIPv6 {
			ipv6Miss = true
		}
		ipSet, ok = sr.svcIPv6Map.Get(req)
	}

	if ok && len(ipSet) > 0 {
		// this map is to avoid IP duplicates, this can happen during a transition period where 2 services are using the same IP
		noDup := make(map[string]bool)
		var ipLocal []net.IP
		for _, ip := range ipSet {
			if _, dup := noDup[ip.ip]; !dup {
				noDup[ip.ip] = true
				ipLocal = append(ipLocal, net.ParseIP(ip.ip))
			}
		}
		return ipLocal, ok
	}

	return nil, ipv6Miss
}

func (n *network) HandleQueryResp(name string, ip net.IP) {
	networkID := n.ID()
	c := n.getController()
	c.mu.Lock()
	defer c.mu.Unlock()
	sr, ok := c.svcRecords[networkID]

	if !ok {
		return
	}

	ipStr := netutils.ReverseIP(ip.String())
	// If an object with extResolver == true is already in the set this call will fail
	// but anyway it means that has already been inserted before
	if ok, _ := sr.ipMap.Contains(ipStr, ipInfo{name: name}); ok {
		sr.ipMap.Remove(ipStr, ipInfo{name: name})
		sr.ipMap.Insert(ipStr, ipInfo{name: name, extResolver: true})
	}
}

func (n *network) ResolveIP(ip string) string {
	networkID := n.ID()
	c := n.getController()
	c.mu.Lock()
	defer c.mu.Unlock()
	sr, ok := c.svcRecords[networkID]

	if !ok {
		return ""
	}

	nwName := n.Name()

	elemSet, ok := sr.ipMap.Get(ip)
	if !ok || len(elemSet) == 0 {
		return ""
	}
	// NOTE it is possible to have more than one element in the Set, this will happen
	// because of interleave of different events from different sources (local container create vs
	// network db notifications)
	// In such cases the resolution will be based on the first element of the set, and can vary
	// during the system stabilitation
	elem := elemSet[0]
	if elem.extResolver {
		return ""
	}

	return elem.name + "." + nwName
}

func (n *network) ResolveService(name string) ([]*net.SRV, []net.IP) {
	c := n.getController()

	srv := []*net.SRV{}
	ip := []net.IP{}

	logrus.Debugf("Service name To resolve: %v", name)

	// There are DNS implementations that allow SRV queries for names not in
	// the format defined by RFC 2782. Hence specific validations checks are
	// not done
	parts := strings.Split(name, ".")
	if len(parts) < 3 {
		return nil, nil
	}

	portName := parts[0]
	proto := parts[1]
	svcName := strings.Join(parts[2:], ".")

	networkID := n.ID()
	c.mu.Lock()
	defer c.mu.Unlock()
	sr, ok := c.svcRecords[networkID]

	if !ok {
		return nil, nil
	}

	svcs, ok := sr.service[svcName]
	if !ok {
		return nil, nil
	}

	for _, svc := range svcs {
		if svc.portName != portName {
			continue
		}
		if svc.proto != proto {
			continue
		}
		for _, t := range svc.target {
			srv = append(srv,
				&net.SRV{
					Target: t.name,
					Port:   t.port,
				})

			ip = append(ip, t.ip)
		}
	}

	return srv, ip
}

func (n *network) ExecFunc(f func()) error {
	return types.NotImplementedErrorf("ExecFunc not supported by network")
}

func (n *network) NdotsSet() bool {
	return false
}

// config-only network is looked up by name
func (c *Controller) getConfigNetwork(name string) (*network, error) {
	var n Network

	s := func(current Network) bool {
		if current.Info().ConfigOnly() && current.Name() == name {
			n = current
			return true
		}
		return false
	}

	c.WalkNetworks(s)

	if n == nil {
		return nil, types.NotFoundErrorf("configuration network %q not found", name)
	}

	return n.(*network), nil
}

func (n *network) lbSandboxName() string {
	name := "lb-" + n.name
	if n.ingress {
		name = n.name + "-sbox"
	}
	return name
}

func (n *network) lbEndpointName() string {
	return n.name + "-endpoint"
}

func (n *network) createLoadBalancerSandbox() (retErr error) {
	sandboxName := n.lbSandboxName()
	// Mark the sandbox to be a load balancer
	sbOptions := []SandboxOption{OptionLoadBalancer(n.id)}
	if n.ingress {
		sbOptions = append(sbOptions, OptionIngress())
	}
	sb, err := n.ctrlr.NewSandbox(sandboxName, sbOptions...)
	if err != nil {
		return err
	}
	defer func() {
		if retErr != nil {
			if e := n.ctrlr.SandboxDestroy(sandboxName); e != nil {
				logrus.Warnf("could not delete sandbox %s on failure on failure (%v): %v", sandboxName, retErr, e)
			}
		}
	}()

	endpointName := n.lbEndpointName()
	epOptions := []EndpointOption{
		CreateOptionIpam(n.loadBalancerIP, nil, nil, nil),
		CreateOptionLoadBalancer(),
	}
	if n.hasLoadBalancerEndpoint() && !n.ingress {
		// Mark LB endpoints as anonymous so they don't show up in DNS
		epOptions = append(epOptions, CreateOptionAnonymous())
	}
	ep, err := n.createEndpoint(endpointName, epOptions...)
	if err != nil {
		return err
	}
	defer func() {
		if retErr != nil {
			if e := ep.Delete(true); e != nil {
				logrus.Warnf("could not delete endpoint %s on failure on failure (%v): %v", endpointName, retErr, e)
			}
		}
	}()

	if err := ep.Join(sb, nil); err != nil {
		return err
	}

	return sb.EnableService()
}

func (n *network) deleteLoadBalancerSandbox() error {
	n.mu.Lock()
	c := n.ctrlr
	name := n.name
	n.mu.Unlock()

	sandboxName := n.lbSandboxName()
	endpointName := n.lbEndpointName()

	endpoint, err := n.EndpointByName(endpointName)
	if err != nil {
		logrus.Warnf("Failed to find load balancer endpoint %s on network %s: %v", endpointName, name, err)
	} else {
		info := endpoint.Info()
		if info != nil {
			sb := info.Sandbox()
			if sb != nil {
				if err := sb.DisableService(); err != nil {
					logrus.Warnf("Failed to disable service on sandbox %s: %v", sandboxName, err)
					// Ignore error and attempt to delete the load balancer endpoint
				}
			}
		}

		if err := endpoint.Delete(true); err != nil {
			logrus.Warnf("Failed to delete endpoint %s (%s) in %s: %v", endpoint.Name(), endpoint.ID(), sandboxName, err)
			// Ignore error and attempt to delete the sandbox.
		}
	}

	if err := c.SandboxDestroy(sandboxName); err != nil {
		return fmt.Errorf("Failed to delete %s sandbox: %v", sandboxName, err)
	}
	return nil
}

Unrelated to your PR; do you know why DNS entries are stored in the controller instead of the network? I'm wondering if things could be made the other way around, as the current implementation requires the controller to be locked each and every time a DNS entry is updated or a DNS query is handled. This looks wrong from my point of view.