/* Package libnetwork provides the basic functionality and extension points to create network namespaces and allocate interfaces for containers to use. networkType := "bridge" // Create a new controller instance driverOptions := options.Generic{} genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = driverOptions controller, err := libnetwork.New(config.OptionDriverConfig(networkType, genericOption)) if err != nil { return } // Create a network for containers to join. // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make use of network, err := controller.NewNetwork(networkType, "network1", "") if err != nil { return } // For each new container: allocate IP and interfaces. The returned network // settings will be used for container infos (inspect and such), as well as // iptables rules for port publishing. This info is contained or accessible // from the returned endpoint. ep, err := network.CreateEndpoint("Endpoint1") if err != nil { return } // Create the sandbox for the container. // NewSandbox accepts Variadic optional arguments which libnetwork can use. sbx, err := controller.NewSandbox("container1", libnetwork.OptionHostname("test"), libnetwork.OptionDomainname("docker.io")) // A sandbox can join the endpoint via the join api. err = ep.Join(sbx) if err != nil { return } */ package libnetwork import ( "fmt" "net" "path/filepath" "runtime" "strings" "sync" "time" "github.com/docker/docker/libnetwork/cluster" "github.com/docker/docker/libnetwork/config" "github.com/docker/docker/libnetwork/datastore" "github.com/docker/docker/libnetwork/diagnostic" "github.com/docker/docker/libnetwork/discoverapi" "github.com/docker/docker/libnetwork/driverapi" "github.com/docker/docker/libnetwork/drvregistry" "github.com/docker/docker/libnetwork/ipamapi" "github.com/docker/docker/libnetwork/netlabel" "github.com/docker/docker/libnetwork/options" "github.com/docker/docker/libnetwork/osl" "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" "github.com/docker/docker/pkg/stringid" "github.com/moby/locker" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) // NetworkController provides the interface for controller instance which manages // networks. type NetworkController interface { // ID provides a unique identity for the controller ID() string // BuiltinDrivers returns list of builtin drivers BuiltinDrivers() []string // BuiltinIPAMDrivers returns list of builtin ipam drivers BuiltinIPAMDrivers() []string // Config method returns the bootup configuration for the controller Config() config.Config // Create a new network. The options parameter carries network specific options. NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) // Networks returns the list of Network(s) managed by this controller. Networks() []Network // WalkNetworks uses the provided function to walk the Network(s) managed by this controller. WalkNetworks(walker NetworkWalker) // NetworkByName returns the Network which has the passed name. If not found, the error ErrNoSuchNetwork is returned. NetworkByName(name string) (Network, error) // NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned. NetworkByID(id string) (Network, error) // NewSandbox creates a new network sandbox for the passed container id NewSandbox(containerID string, options ...SandboxOption) (Sandbox, error) // Sandboxes returns the list of Sandbox(s) managed by this controller. Sandboxes() []Sandbox // WalkSandboxes uses the provided function to walk the Sandbox(s) managed by this controller. WalkSandboxes(walker SandboxWalker) // SandboxByID returns the Sandbox which has the passed id. If not found, a types.NotFoundError is returned. SandboxByID(id string) (Sandbox, error) // SandboxDestroy destroys a sandbox given a container ID SandboxDestroy(id string) error // Stop network controller Stop() // ReloadConfiguration updates the controller configuration ReloadConfiguration(cfgOptions ...config.Option) error // SetClusterProvider sets cluster provider SetClusterProvider(provider cluster.Provider) // Wait for agent initialization complete in libnetwork controller AgentInitWait() // Wait for agent to stop if running AgentStopWait() // SetKeys configures the encryption key for gossip and overlay data path SetKeys(keys []*types.EncryptionKey) error // StartDiagnostic start the network diagnostic mode StartDiagnostic(port int) // StopDiagnostic start the network diagnostic mode StopDiagnostic() // IsDiagnosticEnabled returns true if the diagnostic is enabled IsDiagnosticEnabled() bool } // NetworkWalker is a client provided function which will be used to walk the Networks. // When the function returns true, the walk will stop. type NetworkWalker func(nw Network) bool // SandboxWalker is a client provided function which will be used to walk the Sandboxes. // When the function returns true, the walk will stop. type SandboxWalker func(sb Sandbox) bool type sandboxTable map[string]*sandbox type controller struct { id string drvRegistry *drvregistry.DrvRegistry sandboxes sandboxTable cfg *config.Config stores []datastore.DataStore extKeyListener net.Listener watchCh chan *endpoint unWatchCh chan *endpoint svcRecords map[string]svcInfo nmap map[string]*netWatch serviceBindings map[serviceKey]*service defOsSbox osl.Sandbox ingressSandbox *sandbox sboxOnce sync.Once agent *agent networkLocker *locker.Locker agentInitDone chan struct{} agentStopDone chan struct{} keys []*types.EncryptionKey DiagnosticServer *diagnostic.Server sync.Mutex } type initializer struct { fn drvregistry.InitFunc ntype string } // New creates a new instance of network controller. func New(cfgOptions ...config.Option) (NetworkController, error) { c := &controller{ id: stringid.GenerateRandomID(), cfg: config.New(cfgOptions...), sandboxes: sandboxTable{}, svcRecords: make(map[string]svcInfo), serviceBindings: make(map[serviceKey]*service), agentInitDone: make(chan struct{}), networkLocker: locker.New(), DiagnosticServer: diagnostic.New(), } c.DiagnosticServer.Init() if err := c.initStores(); err != nil { return nil, err } drvRegistry, err := drvregistry.New(c.getStore(datastore.LocalScope), c.getStore(datastore.GlobalScope), c.RegisterDriver, nil, c.cfg.PluginGetter) if err != nil { return nil, err } for _, i := range getInitializers() { var dcfg map[string]interface{} // External plugins don't need config passed through daemon. They can // bootstrap themselves if i.ntype != "remote" { dcfg = c.makeDriverConfig(i.ntype) } if err := drvRegistry.AddDriver(i.ntype, i.fn, dcfg); err != nil { return nil, err } } if err = initIPAMDrivers(drvRegistry, nil, c.getStore(datastore.GlobalScope), c.cfg.DefaultAddressPool); err != nil { return nil, err } c.drvRegistry = drvRegistry c.WalkNetworks(populateSpecial) // Reserve pools first before doing cleanup. Otherwise the // cleanups of endpoint/network and sandbox below will // generate many unnecessary warnings c.reservePools() // Cleanup resources c.sandboxCleanup(c.cfg.ActiveSandboxes) c.cleanupLocalEndpoints() c.networkCleanup() if err := c.startExternalKeyListener(); err != nil { return nil, err } setupArrangeUserFilterRule(c) return c, nil } func (c *controller) SetClusterProvider(provider cluster.Provider) { var sameProvider bool c.Lock() // Avoids to spawn multiple goroutine for the same cluster provider if c.cfg.ClusterProvider == provider { // If the cluster provider is already set, there is already a go routine spawned // that is listening for events, so nothing to do here sameProvider = true } else { c.cfg.ClusterProvider = provider } c.Unlock() if provider == nil || sameProvider { return } // We don't want to spawn a new go routine if the previous one did not exit yet c.AgentStopWait() go c.clusterAgentInit() } // libnetwork side of agent depends on the keys. On the first receipt of // keys setup the agent. For subsequent key set handle the key change func (c *controller) SetKeys(keys []*types.EncryptionKey) error { subsysKeys := make(map[string]int) for _, key := range keys { if key.Subsystem != subsysGossip && key.Subsystem != subsysIPSec { return fmt.Errorf("key received for unrecognized subsystem") } subsysKeys[key.Subsystem]++ } for s, count := range subsysKeys { if count != keyringSize { return fmt.Errorf("incorrect number of keys for subsystem %v", s) } } agent := c.getAgent() if agent == nil { c.Lock() c.keys = keys c.Unlock() return nil } return c.handleKeyChange(keys) } func (c *controller) getAgent() *agent { c.Lock() defer c.Unlock() return c.agent } func (c *controller) clusterAgentInit() { clusterProvider := c.cfg.ClusterProvider var keysAvailable bool for { eventType := <-clusterProvider.ListenClusterEvents() // The events: EventSocketChange, EventNodeReady and EventNetworkKeysAvailable are not ordered // when all the condition for the agent initialization are met then proceed with it switch eventType { case cluster.EventNetworkKeysAvailable: // Validates that the keys are actually available before starting the initialization // This will handle old spurious messages left on the channel c.Lock() keysAvailable = c.keys != nil c.Unlock() fallthrough case cluster.EventSocketChange, cluster.EventNodeReady: if keysAvailable && !c.isDistributedControl() { c.agentOperationStart() if err := c.agentSetup(clusterProvider); err != nil { c.agentStopComplete() } else { c.agentInitComplete() } } case cluster.EventNodeLeave: c.agentOperationStart() c.Lock() c.keys = nil c.Unlock() // We are leaving the cluster. Make sure we // close the gossip so that we stop all // incoming gossip updates before cleaning up // any remaining service bindings. But before // deleting the networks since the networks // should still be present when cleaning up // service bindings c.agentClose() c.cleanupServiceDiscovery("") c.cleanupServiceBindings("") c.agentStopComplete() return } } } // AgentInitWait waits for agent initialization to be completed in the controller. func (c *controller) AgentInitWait() { c.Lock() agentInitDone := c.agentInitDone c.Unlock() if agentInitDone != nil { <-agentInitDone } } // AgentStopWait waits for the Agent stop to be completed in the controller func (c *controller) AgentStopWait() { c.Lock() agentStopDone := c.agentStopDone c.Unlock() if agentStopDone != nil { <-agentStopDone } } // agentOperationStart marks the start of an Agent Init or Agent Stop func (c *controller) agentOperationStart() { c.Lock() if c.agentInitDone == nil { c.agentInitDone = make(chan struct{}) } if c.agentStopDone == nil { c.agentStopDone = make(chan struct{}) } c.Unlock() } // agentInitComplete notifies the successful completion of the Agent initialization func (c *controller) agentInitComplete() { c.Lock() if c.agentInitDone != nil { close(c.agentInitDone) c.agentInitDone = nil } c.Unlock() } // agentStopComplete notifies the successful completion of the Agent stop func (c *controller) agentStopComplete() { c.Lock() if c.agentStopDone != nil { close(c.agentStopDone) c.agentStopDone = nil } c.Unlock() } func (c *controller) makeDriverConfig(ntype string) map[string]interface{} { if c.cfg == nil { return nil } config := make(map[string]interface{}) for _, label := range c.cfg.Labels { if !strings.HasPrefix(netlabel.Key(label), netlabel.DriverPrefix+"."+ntype) { continue } config[netlabel.Key(label)] = netlabel.Value(label) } drvCfg, ok := c.cfg.DriverCfg[ntype] if ok { for k, v := range drvCfg.(map[string]interface{}) { config[k] = v } } for k, v := range c.cfg.Scopes { if !v.IsValid() { continue } config[netlabel.MakeKVClient(k)] = discoverapi.DatastoreConfigData{ Scope: k, Provider: v.Client.Provider, Address: v.Client.Address, Config: v.Client.Config, } } return config } var procReloadConfig = make(chan (bool), 1) func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error { procReloadConfig <- true defer func() { <-procReloadConfig }() // For now we accept the configuration reload only as a mean to provide a global store config after boot. // Refuse the configuration if it alters an existing datastore client configuration. update := false cfg := config.New(cfgOptions...) for s := range c.cfg.Scopes { if _, ok := cfg.Scopes[s]; !ok { return types.ForbiddenErrorf("cannot accept new configuration because it removes an existing datastore client") } } for s, nSCfg := range cfg.Scopes { if eSCfg, ok := c.cfg.Scopes[s]; ok { if eSCfg.Client.Provider != nSCfg.Client.Provider || eSCfg.Client.Address != nSCfg.Client.Address { return types.ForbiddenErrorf("cannot accept new configuration because it modifies an existing datastore client") } } else { if err := c.initScopedStore(s, nSCfg); err != nil { return err } update = true } } if !update { return nil } c.Lock() c.cfg = cfg c.Unlock() var dsConfig *discoverapi.DatastoreConfigData for scope, sCfg := range cfg.Scopes { if scope == datastore.LocalScope || !sCfg.IsValid() { continue } dsConfig = &discoverapi.DatastoreConfigData{ Scope: scope, Provider: sCfg.Client.Provider, Address: sCfg.Client.Address, Config: sCfg.Client.Config, } break } if dsConfig == nil { return nil } c.drvRegistry.WalkIPAMs(func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool { err := driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig) if err != nil { logrus.Errorf("Failed to set datastore in driver %s: %v", name, err) } return false }) c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool { err := driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig) if err != nil { logrus.Errorf("Failed to set datastore in driver %s: %v", name, err) } return false }) return nil } func (c *controller) ID() string { return c.id } func (c *controller) BuiltinDrivers() []string { drivers := []string{} c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool { if driver.IsBuiltIn() { drivers = append(drivers, name) } return false }) return drivers } func (c *controller) BuiltinIPAMDrivers() []string { drivers := []string{} c.drvRegistry.WalkIPAMs(func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool { if driver.IsBuiltIn() { drivers = append(drivers, name) } return false }) return drivers } func (c *controller) processNodeDiscovery(nodes []net.IP, add bool) { c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool { c.pushNodeDiscovery(driver, capability, nodes, add) return false }) } func (c *controller) pushNodeDiscovery(d driverapi.Driver, cap driverapi.Capability, nodes []net.IP, add bool) { var self net.IP // try swarm-mode config if agent := c.getAgent(); agent != nil { self = net.ParseIP(agent.advertiseAddr) } if d == nil || cap.ConnectivityScope != datastore.GlobalScope || nodes == nil { return } for _, node := range nodes { nodeData := discoverapi.NodeDiscoveryData{Address: node.String(), Self: node.Equal(self)} var err error if add { err = d.DiscoverNew(discoverapi.NodeDiscovery, nodeData) } else { err = d.DiscoverDelete(discoverapi.NodeDiscovery, nodeData) } if err != nil { logrus.Debugf("discovery notification error: %v", err) } } } func (c *controller) Config() config.Config { c.Lock() defer c.Unlock() if c.cfg == nil { return config.Config{} } return *c.cfg } func (c *controller) isManager() bool { c.Lock() defer c.Unlock() if c.cfg == nil || c.cfg.ClusterProvider == nil { return false } return c.cfg.ClusterProvider.IsManager() } func (c *controller) isAgent() bool { c.Lock() defer c.Unlock() if c.cfg == nil || c.cfg.ClusterProvider == nil { return false } return c.cfg.ClusterProvider.IsAgent() } func (c *controller) isDistributedControl() bool { return !c.isManager() && !c.isAgent() } func (c *controller) GetPluginGetter() plugingetter.PluginGetter { return c.drvRegistry.GetPluginGetter() } func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver, capability driverapi.Capability) error { c.agentDriverNotify(driver) return nil } // XXX This should be made driver agnostic. See comment below. const overlayDSROptionString = "dsr" // NewNetwork creates a new network of the specified network type. The options // are network specific and modeled in a generic way. func (c *controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) { var ( cap *driverapi.Capability err error t *network skipCfgEpCount bool ) if id != "" { c.networkLocker.Lock(id) defer c.networkLocker.Unlock(id) //nolint:errcheck if _, err = c.NetworkByID(id); err == nil { return nil, NetworkNameError(id) } } if !config.IsValidName(name) { return nil, ErrInvalidName(name) } if id == "" { id = stringid.GenerateRandomID() } defaultIpam := defaultIpamForNetworkType(networkType) // Construct the network object network := &network{ name: name, networkType: networkType, generic: map[string]interface{}{netlabel.GenericData: make(map[string]string)}, ipamType: defaultIpam, id: id, created: time.Now(), ctrlr: c, persist: true, drvOnce: &sync.Once{}, loadBalancerMode: loadBalancerModeDefault, } network.processOptions(options...) if err = network.validateConfiguration(); err != nil { return nil, err } // Reset network types, force local scope and skip allocation and // plumbing for configuration networks. Reset of the config-only // network drivers is needed so that this special network is not // usable by old engine versions. if network.configOnly { network.scope = datastore.LocalScope network.networkType = "null" goto addToStore } _, cap, err = network.resolveDriver(network.networkType, true) if err != nil { return nil, err } if network.scope == datastore.LocalScope && cap.DataScope == datastore.GlobalScope { return nil, types.ForbiddenErrorf("cannot downgrade network scope for %s networks", networkType) } if network.ingress && cap.DataScope != datastore.GlobalScope { return nil, types.ForbiddenErrorf("Ingress network can only be global scope network") } // At this point the network scope is still unknown if not set by user if (cap.DataScope == datastore.GlobalScope || network.scope == datastore.SwarmScope) && !c.isDistributedControl() && !network.dynamic { if c.isManager() { // For non-distributed controlled environment, globalscoped non-dynamic networks are redirected to Manager return nil, ManagerRedirectError(name) } return nil, types.ForbiddenErrorf("Cannot create a multi-host network from a worker node. Please create the network from a manager node.") } if network.scope == datastore.SwarmScope && c.isDistributedControl() { return nil, types.ForbiddenErrorf("cannot create a swarm scoped network when swarm is not active") } // Make sure we have a driver available for this network type // before we allocate anything. if _, err := network.driver(true); err != nil { return nil, err } // From this point on, we need the network specific configuration, // which may come from a configuration-only network if network.configFrom != "" { t, err = c.getConfigNetwork(network.configFrom) if err != nil { return nil, types.NotFoundErrorf("configuration network %q does not exist", network.configFrom) } if err = t.applyConfigurationTo(network); err != nil { return nil, types.InternalErrorf("Failed to apply configuration: %v", err) } network.generic[netlabel.Internal] = network.internal defer func() { if err == nil && !skipCfgEpCount { if err := t.getEpCnt().IncEndpointCnt(); err != nil { logrus.Warnf("Failed to update reference count for configuration network %q on creation of network %q: %v", t.Name(), network.Name(), err) } } }() } err = network.ipamAllocate() if err != nil { return nil, err } defer func() { if err != nil { network.ipamRelease() } }() err = c.addNetwork(network) if err != nil { if _, ok := err.(types.MaskableError); ok { //nolint:gosimple // This error can be ignored and set this boolean // value to skip a refcount increment for configOnly networks skipCfgEpCount = true } else { return nil, err } } defer func() { if err != nil { if e := network.deleteNetwork(); e != nil { logrus.Warnf("couldn't roll back driver network on network %s creation failure: %v", network.name, err) } } }() // XXX If the driver type is "overlay" check the options for DSR // being set. If so, set the network's load balancing mode to DSR. // This should really be done in a network option, but due to // time pressure to get this in without adding changes to moby, // swarm and CLI, it is being implemented as a driver-specific // option. Unfortunately, drivers can't influence the core // "libnetwork.network" data type. Hence we need this hack code // to implement in this manner. if gval, ok := network.generic[netlabel.GenericData]; ok && network.networkType == "overlay" { optMap := gval.(map[string]string) if _, ok := optMap[overlayDSROptionString]; ok { network.loadBalancerMode = loadBalancerModeDSR } } addToStore: // First store the endpoint count, then the network. To avoid to // end up with a datastore containing a network and not an epCnt, // in case of an ungraceful shutdown during this function call. epCnt := &endpointCnt{n: network} if err = c.updateToStore(epCnt); err != nil { return nil, err } defer func() { if err != nil { if e := c.deleteFromStore(epCnt); e != nil { logrus.Warnf("could not rollback from store, epCnt %v on failure (%v): %v", epCnt, err, e) } } }() network.epCnt = epCnt if err = c.updateToStore(network); err != nil { return nil, err } defer func() { if err != nil { if e := c.deleteFromStore(network); e != nil { logrus.Warnf("could not rollback from store, network %v on failure (%v): %v", network, err, e) } } }() if network.configOnly { return network, nil } joinCluster(network) defer func() { if err != nil { network.cancelDriverWatches() if e := network.leaveCluster(); e != nil { logrus.Warnf("Failed to leave agent cluster on network %s on failure (%v): %v", network.name, err, e) } } }() if network.hasLoadBalancerEndpoint() { if err = network.createLoadBalancerSandbox(); err != nil { return nil, err } } if !c.isDistributedControl() { c.Lock() arrangeIngressFilterRule() c.Unlock() } arrangeUserFilterRule() return network, nil } var joinCluster NetworkWalker = func(nw Network) bool { n := nw.(*network) if n.configOnly { return false } if err := n.joinCluster(); err != nil { logrus.Errorf("Failed to join network %s (%s) into agent cluster: %v", n.Name(), n.ID(), err) } n.addDriverWatches() return false } func (c *controller) reservePools() { networks, err := c.getNetworksForScope(datastore.LocalScope) if err != nil { logrus.Warnf("Could not retrieve networks from local store during ipam allocation for existing networks: %v", err) return } for _, n := range networks { if n.configOnly { continue } if !doReplayPoolReserve(n) { continue } // Construct pseudo configs for the auto IP case autoIPv4 := (len(n.ipamV4Config) == 0 || (len(n.ipamV4Config) == 1 && n.ipamV4Config[0].PreferredPool == "")) && len(n.ipamV4Info) > 0 autoIPv6 := (len(n.ipamV6Config) == 0 || (len(n.ipamV6Config) == 1 && n.ipamV6Config[0].PreferredPool == "")) && len(n.ipamV6Info) > 0 if autoIPv4 { n.ipamV4Config = []*IpamConf{{PreferredPool: n.ipamV4Info[0].Pool.String()}} } if n.enableIPv6 && autoIPv6 { n.ipamV6Config = []*IpamConf{{PreferredPool: n.ipamV6Info[0].Pool.String()}} } // Account current network gateways for i, c := range n.ipamV4Config { if c.Gateway == "" && n.ipamV4Info[i].Gateway != nil { c.Gateway = n.ipamV4Info[i].Gateway.IP.String() } } if n.enableIPv6 { for i, c := range n.ipamV6Config { if c.Gateway == "" && n.ipamV6Info[i].Gateway != nil { c.Gateway = n.ipamV6Info[i].Gateway.IP.String() } } } // Reserve pools if err := n.ipamAllocate(); err != nil { logrus.Warnf("Failed to allocate ipam pool(s) for network %q (%s): %v", n.Name(), n.ID(), err) } // Reserve existing endpoints' addresses ipam, _, err := n.getController().getIPAMDriver(n.ipamType) if err != nil { logrus.Warnf("Failed to retrieve ipam driver for network %q (%s) during address reservation", n.Name(), n.ID()) continue } epl, err := n.getEndpointsFromStore() if err != nil { logrus.Warnf("Failed to retrieve list of current endpoints on network %q (%s)", n.Name(), n.ID()) continue } for _, ep := range epl { if ep.Iface() == nil { logrus.Warnf("endpoint interface is empty for %q (%s)", ep.Name(), ep.ID()) continue } if err := ep.assignAddress(ipam, true, ep.Iface().AddressIPv6() != nil); err != nil { logrus.Warnf("Failed to reserve current address for endpoint %q (%s) on network %q (%s)", ep.Name(), ep.ID(), n.Name(), n.ID()) } } } } func doReplayPoolReserve(n *network) bool { _, caps, err := n.getController().getIPAMDriver(n.ipamType) if err != nil { logrus.Warnf("Failed to retrieve ipam driver for network %q (%s): %v", n.Name(), n.ID(), err) return false } return caps.RequiresRequestReplay } func (c *controller) addNetwork(n *network) error { d, err := n.driver(true) if err != nil { return err } // Create the network if err := d.CreateNetwork(n.id, n.generic, n, n.getIPData(4), n.getIPData(6)); err != nil { return err } n.startResolver() return nil } func (c *controller) Networks() []Network { var list []Network for _, n := range c.getNetworksFromStore() { if n.inDelete { continue } list = append(list, n) } return list } func (c *controller) WalkNetworks(walker NetworkWalker) { for _, n := range c.Networks() { if walker(n) { return } } } func (c *controller) NetworkByName(name string) (Network, error) { if name == "" { return nil, ErrInvalidName(name) } var n Network s := func(current Network) bool { if current.Name() == name { n = current return true } return false } c.WalkNetworks(s) if n == nil { return nil, ErrNoSuchNetwork(name) } return n, nil } func (c *controller) NetworkByID(id string) (Network, error) { if id == "" { return nil, ErrInvalidID(id) } n, err := c.getNetworkFromStore(id) if err != nil { return nil, ErrNoSuchNetwork(id) } return n, nil } // NewSandbox creates a new sandbox for the passed container id func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (Sandbox, error) { if containerID == "" { return nil, types.BadRequestErrorf("invalid container ID") } var sb *sandbox c.Lock() for _, s := range c.sandboxes { if s.containerID == containerID { // If not a stub, then we already have a complete sandbox. if !s.isStub { sbID := s.ID() c.Unlock() return nil, types.ForbiddenErrorf("container %s is already present in sandbox %s", containerID, sbID) } // We already have a stub sandbox from the // store. Make use of it so that we don't lose // the endpoints from store but reset the // isStub flag. sb = s sb.isStub = false break } } c.Unlock() sandboxID := stringid.GenerateRandomID() if runtime.GOOS == "windows" { sandboxID = containerID } // Create sandbox and process options first. Key generation depends on an option if sb == nil { sb = &sandbox{ id: sandboxID, containerID: containerID, endpoints: []*endpoint{}, epPriority: map[string]int{}, populatedEndpoints: map[string]struct{}{}, config: containerConfig{}, controller: c, extDNS: []extDNSEntry{}, } } sb.processOptions(options...) c.Lock() if sb.ingress && c.ingressSandbox != nil { c.Unlock() return nil, types.ForbiddenErrorf("ingress sandbox already present") } if sb.ingress { c.ingressSandbox = sb sb.config.hostsPath = filepath.Join(c.cfg.DataDir, "/network/files/hosts") sb.config.resolvConfPath = filepath.Join(c.cfg.DataDir, "/network/files/resolv.conf") sb.id = "ingress_sbox" } else if sb.loadBalancerNID != "" { sb.id = "lb_" + sb.loadBalancerNID } c.Unlock() var err error defer func() { if err != nil { c.Lock() if sb.ingress { c.ingressSandbox = nil } c.Unlock() } }() if err = sb.setupResolutionFiles(); err != nil { return nil, err } if sb.config.useDefaultSandBox { c.sboxOnce.Do(func() { c.defOsSbox, err = osl.NewSandbox(sb.Key(), false, false) }) if err != nil { c.sboxOnce = sync.Once{} return nil, fmt.Errorf("failed to create default sandbox: %v", err) } sb.osSbox = c.defOsSbox } if sb.osSbox == nil && !sb.config.useExternalKey { if sb.osSbox, err = osl.NewSandbox(sb.Key(), !sb.config.useDefaultSandBox, false); err != nil { return nil, fmt.Errorf("failed to create new osl sandbox: %v", err) } } if sb.osSbox != nil { // Apply operating specific knobs on the load balancer sandbox err := sb.osSbox.InvokeFunc(func() { sb.osSbox.ApplyOSTweaks(sb.oslTypes) }) if err != nil { logrus.Errorf("Failed to apply performance tuning sysctls to the sandbox: %v", err) } // Keep this just so performance is not changed sb.osSbox.ApplyOSTweaks(sb.oslTypes) } c.Lock() c.sandboxes[sb.id] = sb c.Unlock() defer func() { if err != nil { c.Lock() delete(c.sandboxes, sb.id) c.Unlock() } }() err = sb.storeUpdate() if err != nil { return nil, fmt.Errorf("failed to update the store state of sandbox: %v", err) } return sb, nil } func (c *controller) Sandboxes() []Sandbox { c.Lock() defer c.Unlock() list := make([]Sandbox, 0, len(c.sandboxes)) for _, s := range c.sandboxes { // Hide stub sandboxes from libnetwork users if s.isStub { continue } list = append(list, s) } return list } func (c *controller) WalkSandboxes(walker SandboxWalker) { for _, sb := range c.Sandboxes() { if walker(sb) { return } } } func (c *controller) SandboxByID(id string) (Sandbox, error) { if id == "" { return nil, ErrInvalidID(id) } c.Lock() s, ok := c.sandboxes[id] c.Unlock() if !ok { return nil, types.NotFoundErrorf("sandbox %s not found", id) } return s, nil } // SandboxDestroy destroys a sandbox given a container ID func (c *controller) SandboxDestroy(id string) error { var sb *sandbox c.Lock() for _, s := range c.sandboxes { if s.containerID == id { sb = s break } } c.Unlock() // It is not an error if sandbox is not available if sb == nil { return nil } return sb.Delete() } // SandboxContainerWalker returns a Sandbox Walker function which looks for an existing Sandbox with the passed containerID func SandboxContainerWalker(out *Sandbox, containerID string) SandboxWalker { return func(sb Sandbox) bool { if sb.ContainerID() == containerID { *out = sb return true } return false } } // SandboxKeyWalker returns a Sandbox Walker function which looks for an existing Sandbox with the passed key func SandboxKeyWalker(out *Sandbox, key string) SandboxWalker { return func(sb Sandbox) bool { if sb.Key() == key { *out = sb return true } return false } } func (c *controller) loadDriver(networkType string) error { var err error if pg := c.GetPluginGetter(); pg != nil { _, err = pg.Get(networkType, driverapi.NetworkPluginEndpointType, plugingetter.Lookup) } else { _, err = plugins.Get(networkType, driverapi.NetworkPluginEndpointType) } if err != nil { if errors.Cause(err) == plugins.ErrNotFound { return types.NotFoundErrorf(err.Error()) } return err } return nil } func (c *controller) loadIPAMDriver(name string) error { var err error if pg := c.GetPluginGetter(); pg != nil { _, err = pg.Get(name, ipamapi.PluginEndpointType, plugingetter.Lookup) } else { _, err = plugins.Get(name, ipamapi.PluginEndpointType) } if err != nil { if errors.Cause(err) == plugins.ErrNotFound { return types.NotFoundErrorf(err.Error()) } return err } return nil } func (c *controller) getIPAMDriver(name string) (ipamapi.Ipam, *ipamapi.Capability, error) { id, cap := c.drvRegistry.IPAM(name) if id == nil { // Might be a plugin name. Try loading it if err := c.loadIPAMDriver(name); err != nil { return nil, nil, err } // Now that we resolved the plugin, try again looking up the registry id, cap = c.drvRegistry.IPAM(name) if id == nil { return nil, nil, types.BadRequestErrorf("invalid ipam driver: %q", name) } } return id, cap, nil } func (c *controller) Stop() { c.closeStores() c.stopExternalKeyListener() osl.GC() } // StartDiagnostic start the network dias mode func (c *controller) StartDiagnostic(port int) { c.Lock() if !c.DiagnosticServer.IsDiagnosticEnabled() { c.DiagnosticServer.EnableDiagnostic("127.0.0.1", port) } c.Unlock() } // StopDiagnostic start the network dias mode func (c *controller) StopDiagnostic() { c.Lock() if c.DiagnosticServer.IsDiagnosticEnabled() { c.DiagnosticServer.DisableDiagnostic() } c.Unlock() } // IsDiagnosticEnabled returns true if the dias is enabled func (c *controller) IsDiagnosticEnabled() bool { c.Lock() defer c.Unlock() return c.DiagnosticServer.IsDiagnosticEnabled() } func (c *controller) iptablesEnabled() bool { c.Lock() defer c.Unlock() if c.cfg == nil { return false } // parse map cfg["bridge"]["generic"]["EnableIPTable"] cfgBridge, ok := c.cfg.DriverCfg["bridge"].(map[string]interface{}) if !ok { return false } cfgGeneric, ok := cfgBridge[netlabel.GenericData].(options.Generic) if !ok { return false } enabled, ok := cfgGeneric["EnableIPTables"].(bool) if !ok { // unless user explicitly stated, assume iptable is enabled enabled = true } return enabled }