github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/daemon/container_operations.go (about) 1 // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: 2 //go:build go1.19 3 4 package daemon // import "github.com/docker/docker/daemon" 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "net" 11 "os" 12 "path" 13 "strings" 14 "time" 15 16 "github.com/containerd/log" 17 containertypes "github.com/docker/docker/api/types/container" 18 "github.com/docker/docker/api/types/events" 19 networktypes "github.com/docker/docker/api/types/network" 20 "github.com/docker/docker/container" 21 "github.com/docker/docker/daemon/config" 22 "github.com/docker/docker/daemon/network" 23 "github.com/docker/docker/errdefs" 24 "github.com/docker/docker/internal/multierror" 25 "github.com/docker/docker/internal/sliceutil" 26 "github.com/docker/docker/libnetwork" 27 "github.com/docker/docker/libnetwork/netlabel" 28 "github.com/docker/docker/libnetwork/options" 29 "github.com/docker/docker/libnetwork/scope" 30 "github.com/docker/docker/libnetwork/types" 31 "github.com/docker/docker/opts" 32 "github.com/docker/docker/pkg/stringid" 33 "github.com/docker/docker/runconfig" 34 "github.com/docker/go-connections/nat" 35 ) 36 37 func ipAddresses(ips []net.IP) []string { 38 var addrs []string 39 for _, ip := range ips { 40 addrs = append(addrs, ip.String()) 41 } 42 return addrs 43 } 44 45 func (daemon *Daemon) buildSandboxOptions(cfg *config.Config, container *container.Container) ([]libnetwork.SandboxOption, error) { 46 var sboxOptions []libnetwork.SandboxOption 47 sboxOptions = append(sboxOptions, libnetwork.OptionHostname(container.Config.Hostname), libnetwork.OptionDomainname(container.Config.Domainname)) 48 49 if container.HostConfig.NetworkMode.IsHost() { 50 sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) 51 } else { 52 // OptionUseExternalKey is mandatory for userns support. 53 // But optional for non-userns support 54 sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey()) 55 } 56 57 if err := setupPathsAndSandboxOptions(container, cfg, &sboxOptions); err != nil { 58 return nil, err 59 } 60 61 if len(container.HostConfig.DNS) > 0 { 62 sboxOptions = append(sboxOptions, libnetwork.OptionDNS(container.HostConfig.DNS)) 63 } else if len(cfg.DNS) > 0 { 64 sboxOptions = append(sboxOptions, libnetwork.OptionDNS(ipAddresses(cfg.DNS))) 65 } 66 if len(container.HostConfig.DNSSearch) > 0 { 67 sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(container.HostConfig.DNSSearch)) 68 } else if len(cfg.DNSSearch) > 0 { 69 sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(cfg.DNSSearch)) 70 } 71 if len(container.HostConfig.DNSOptions) > 0 { 72 sboxOptions = append(sboxOptions, libnetwork.OptionDNSOptions(container.HostConfig.DNSOptions)) 73 } else if len(cfg.DNSOptions) > 0 { 74 sboxOptions = append(sboxOptions, libnetwork.OptionDNSOptions(cfg.DNSOptions)) 75 } 76 77 for _, extraHost := range container.HostConfig.ExtraHosts { 78 // allow IPv6 addresses in extra hosts; only split on first ":" 79 if _, err := opts.ValidateExtraHost(extraHost); err != nil { 80 return nil, err 81 } 82 host, ip, _ := strings.Cut(extraHost, ":") 83 // If the IP Address is a string called "host-gateway", replace this 84 // value with the IP address stored in the daemon level HostGatewayIP 85 // config variable 86 if ip == opts.HostGatewayName { 87 gateway := cfg.HostGatewayIP.String() 88 if gateway == "" { 89 return nil, fmt.Errorf("unable to derive the IP value for host-gateway") 90 } 91 ip = gateway 92 } 93 sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(host, ip)) 94 } 95 96 bindings := make(nat.PortMap) 97 if container.HostConfig.PortBindings != nil { 98 for p, b := range container.HostConfig.PortBindings { 99 bindings[p] = []nat.PortBinding{} 100 for _, bb := range b { 101 bindings[p] = append(bindings[p], nat.PortBinding{ 102 HostIP: bb.HostIP, 103 HostPort: bb.HostPort, 104 }) 105 } 106 } 107 } 108 109 // TODO(thaJeztah): Move this code to a method on nat.PortSet. 110 ports := make([]nat.Port, 0, len(container.Config.ExposedPorts)) 111 for p := range container.Config.ExposedPorts { 112 ports = append(ports, p) 113 } 114 nat.SortPortMap(ports, bindings) 115 116 var ( 117 publishedPorts []types.PortBinding 118 exposedPorts []types.TransportPort 119 ) 120 for _, port := range ports { 121 portProto := types.ParseProtocol(port.Proto()) 122 portNum := uint16(port.Int()) 123 exposedPorts = append(exposedPorts, types.TransportPort{ 124 Proto: portProto, 125 Port: portNum, 126 }) 127 128 for _, binding := range bindings[port] { 129 newP, err := nat.NewPort(nat.SplitProtoPort(binding.HostPort)) 130 var portStart, portEnd int 131 if err == nil { 132 portStart, portEnd, err = newP.Range() 133 } 134 if err != nil { 135 return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding.HostPort, err) 136 } 137 publishedPorts = append(publishedPorts, types.PortBinding{ 138 Proto: portProto, 139 Port: portNum, 140 HostIP: net.ParseIP(binding.HostIP), 141 HostPort: uint16(portStart), 142 HostPortEnd: uint16(portEnd), 143 }) 144 } 145 146 if container.HostConfig.PublishAllPorts && len(bindings[port]) == 0 { 147 publishedPorts = append(publishedPorts, types.PortBinding{ 148 Proto: portProto, 149 Port: portNum, 150 }) 151 } 152 } 153 154 sboxOptions = append(sboxOptions, libnetwork.OptionPortMapping(publishedPorts), libnetwork.OptionExposedPorts(exposedPorts)) 155 156 // Legacy Link feature is supported only for the default bridge network. 157 // return if this call to build join options is not for default bridge network 158 // Legacy Link is only supported by docker run --link 159 defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() 160 bridgeSettings, ok := container.NetworkSettings.Networks[defaultNetName] 161 if !ok || bridgeSettings.EndpointSettings == nil || bridgeSettings.EndpointID == "" { 162 return sboxOptions, nil 163 } 164 165 var ( 166 childEndpoints []string 167 cEndpointID string 168 ) 169 for linkAlias, child := range daemon.children(container) { 170 if !isLinkable(child) { 171 return nil, fmt.Errorf("Cannot link to %s, as it does not belong to the default network", child.Name) 172 } 173 _, alias := path.Split(linkAlias) 174 // allow access to the linked container via the alias, real name, and container hostname 175 aliasList := alias + " " + child.Config.Hostname 176 // only add the name if alias isn't equal to the name 177 if alias != child.Name[1:] { 178 aliasList = aliasList + " " + child.Name[1:] 179 } 180 defaultNW := child.NetworkSettings.Networks[defaultNetName] 181 if defaultNW.IPAddress != "" { 182 sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, defaultNW.IPAddress)) 183 } 184 if defaultNW.GlobalIPv6Address != "" { 185 sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, defaultNW.GlobalIPv6Address)) 186 } 187 cEndpointID = defaultNW.EndpointID 188 if cEndpointID != "" { 189 childEndpoints = append(childEndpoints, cEndpointID) 190 } 191 } 192 193 var parentEndpoints []string 194 for alias, parent := range daemon.parents(container) { 195 if cfg.DisableBridge || !container.HostConfig.NetworkMode.IsPrivate() { 196 continue 197 } 198 199 _, alias = path.Split(alias) 200 log.G(context.TODO()).Debugf("Update /etc/hosts of %s for alias %s with ip %s", parent.ID, alias, bridgeSettings.IPAddress) 201 sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(parent.ID, alias, bridgeSettings.IPAddress)) 202 if cEndpointID != "" { 203 parentEndpoints = append(parentEndpoints, cEndpointID) 204 } 205 } 206 207 sboxOptions = append(sboxOptions, libnetwork.OptionGeneric(options.Generic{ 208 netlabel.GenericData: options.Generic{ 209 "ParentEndpoints": parentEndpoints, 210 "ChildEndpoints": childEndpoints, 211 }, 212 })) 213 return sboxOptions, nil 214 } 215 216 func (daemon *Daemon) updateNetworkSettings(container *container.Container, n *libnetwork.Network, endpointConfig *networktypes.EndpointSettings) error { 217 if container.NetworkSettings == nil { 218 container.NetworkSettings = &network.Settings{} 219 } 220 if container.NetworkSettings.Networks == nil { 221 container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings) 222 } 223 224 if !container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() { 225 return runconfig.ErrConflictHostNetwork 226 } 227 228 for s, v := range container.NetworkSettings.Networks { 229 sn, err := daemon.FindNetwork(getNetworkID(s, v.EndpointSettings)) 230 if err != nil { 231 continue 232 } 233 234 if sn.Name() == n.Name() { 235 // If the network scope is swarm, then this 236 // is an attachable network, which may not 237 // be locally available previously. 238 // So always update. 239 if n.Scope() == scope.Swarm { 240 continue 241 } 242 // Avoid duplicate config 243 return nil 244 } 245 if !containertypes.NetworkMode(sn.Type()).IsPrivate() || 246 !containertypes.NetworkMode(n.Type()).IsPrivate() { 247 return runconfig.ErrConflictSharedNetwork 248 } 249 if containertypes.NetworkMode(sn.Name()).IsNone() || 250 containertypes.NetworkMode(n.Name()).IsNone() { 251 return runconfig.ErrConflictNoNetwork 252 } 253 } 254 255 container.NetworkSettings.Networks[n.Name()] = &network.EndpointSettings{ 256 EndpointSettings: endpointConfig, 257 } 258 259 return nil 260 } 261 262 func (daemon *Daemon) updateEndpointNetworkSettings(cfg *config.Config, container *container.Container, n *libnetwork.Network, ep *libnetwork.Endpoint) error { 263 if err := buildEndpointInfo(container.NetworkSettings, n, ep); err != nil { 264 return err 265 } 266 267 if container.HostConfig.NetworkMode == runconfig.DefaultDaemonNetworkMode() { 268 container.NetworkSettings.Bridge = cfg.BridgeConfig.Iface 269 } 270 271 return nil 272 } 273 274 // UpdateNetwork is used to update the container's network (e.g. when linked containers 275 // get removed/unlinked). 276 func (daemon *Daemon) updateNetwork(cfg *config.Config, container *container.Container) error { 277 var ( 278 start = time.Now() 279 ctrl = daemon.netController 280 sid = container.NetworkSettings.SandboxID 281 ) 282 283 sb, err := ctrl.SandboxByID(sid) 284 if err != nil { 285 return fmt.Errorf("error locating sandbox id %s: %v", sid, err) 286 } 287 288 // Find if container is connected to the default bridge network 289 var n *libnetwork.Network 290 for name, v := range container.NetworkSettings.Networks { 291 sn, err := daemon.FindNetwork(getNetworkID(name, v.EndpointSettings)) 292 if err != nil { 293 continue 294 } 295 if sn.Name() == runconfig.DefaultDaemonNetworkMode().NetworkName() { 296 n = sn 297 break 298 } 299 } 300 301 if n == nil { 302 // Not connected to the default bridge network; Nothing to do 303 return nil 304 } 305 306 sbOptions, err := daemon.buildSandboxOptions(cfg, container) 307 if err != nil { 308 return fmt.Errorf("Update network failed: %v", err) 309 } 310 311 if err := sb.Refresh(sbOptions...); err != nil { 312 return fmt.Errorf("Update network failed: Failure in refresh sandbox %s: %v", sid, err) 313 } 314 315 networkActions.WithValues("update").UpdateSince(start) 316 317 return nil 318 } 319 320 func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrName string, epConfig *networktypes.EndpointSettings) (*libnetwork.Network, *networktypes.NetworkingConfig, error) { 321 id := getNetworkID(idOrName, epConfig) 322 323 n, err := daemon.FindNetwork(id) 324 if err != nil { 325 // We should always be able to find the network for a managed container. 326 if container.Managed { 327 return nil, nil, err 328 } 329 } 330 331 // If we found a network and if it is not dynamically created 332 // we should never attempt to attach to that network here. 333 if n != nil { 334 if container.Managed || !n.Dynamic() { 335 return n, nil, nil 336 } 337 // Throw an error if the container is already attached to the network 338 if container.NetworkSettings.Networks != nil { 339 networkName := n.Name() 340 containerName := strings.TrimPrefix(container.Name, "/") 341 if nw, ok := container.NetworkSettings.Networks[networkName]; ok && nw.EndpointID != "" { 342 err := fmt.Errorf("%s is already attached to network %s", containerName, networkName) 343 return n, nil, errdefs.Conflict(err) 344 } 345 } 346 } 347 348 var addresses []string 349 if epConfig != nil && epConfig.IPAMConfig != nil { 350 if epConfig.IPAMConfig.IPv4Address != "" { 351 addresses = append(addresses, epConfig.IPAMConfig.IPv4Address) 352 } 353 if epConfig.IPAMConfig.IPv6Address != "" { 354 addresses = append(addresses, epConfig.IPAMConfig.IPv6Address) 355 } 356 } 357 358 if n == nil && daemon.attachableNetworkLock != nil { 359 daemon.attachableNetworkLock.Lock(id) 360 defer daemon.attachableNetworkLock.Unlock(id) 361 } 362 363 retryCount := 0 364 var nwCfg *networktypes.NetworkingConfig 365 for { 366 // In all other cases, attempt to attach to the network to 367 // trigger attachment in the swarm cluster manager. 368 if daemon.clusterProvider != nil { 369 var err error 370 nwCfg, err = daemon.clusterProvider.AttachNetwork(id, container.ID, addresses) 371 if err != nil { 372 return nil, nil, err 373 } 374 } 375 376 n, err = daemon.FindNetwork(id) 377 if err != nil { 378 if daemon.clusterProvider != nil { 379 if err := daemon.clusterProvider.DetachNetwork(id, container.ID); err != nil { 380 log.G(context.TODO()).Warnf("Could not rollback attachment for container %s to network %s: %v", container.ID, idOrName, err) 381 } 382 } 383 384 // Retry network attach again if we failed to 385 // find the network after successful 386 // attachment because the only reason that 387 // would happen is if some other container 388 // attached to the swarm scope network went down 389 // and removed the network while we were in 390 // the process of attaching. 391 if nwCfg != nil { 392 if _, ok := err.(libnetwork.ErrNoSuchNetwork); ok { 393 if retryCount >= 5 { 394 return nil, nil, fmt.Errorf("could not find network %s after successful attachment", idOrName) 395 } 396 retryCount++ 397 continue 398 } 399 } 400 401 return nil, nil, err 402 } 403 404 break 405 } 406 407 // This container has attachment to a swarm scope 408 // network. Update the container network settings accordingly. 409 container.NetworkSettings.HasSwarmEndpoint = true 410 return n, nwCfg, nil 411 } 412 413 // updateContainerNetworkSettings updates the network settings 414 func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container, endpointsConfig map[string]*networktypes.EndpointSettings) { 415 var n *libnetwork.Network 416 417 mode := container.HostConfig.NetworkMode 418 if container.Config.NetworkDisabled || mode.IsContainer() { 419 return 420 } 421 422 networkName := mode.NetworkName() 423 if mode.IsDefault() { 424 networkName = daemon.netController.Config().DefaultNetwork 425 } 426 427 if mode.IsUserDefined() { 428 var err error 429 430 n, err = daemon.FindNetwork(networkName) 431 if err == nil { 432 networkName = n.Name() 433 } 434 } 435 436 if container.NetworkSettings == nil { 437 container.NetworkSettings = &network.Settings{} 438 } 439 440 if len(endpointsConfig) > 0 { 441 if container.NetworkSettings.Networks == nil { 442 container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings) 443 } 444 445 for name, epConfig := range endpointsConfig { 446 container.NetworkSettings.Networks[name] = &network.EndpointSettings{ 447 EndpointSettings: epConfig, 448 // At this point, during container creation, epConfig.MacAddress is the 449 // configured value from the API. If there is no configured value, the 450 // same field will later be used to store a generated MAC address. So, 451 // remember the requested address now. 452 DesiredMacAddress: epConfig.MacAddress, 453 } 454 } 455 } 456 457 if container.NetworkSettings.Networks == nil { 458 container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings) 459 container.NetworkSettings.Networks[networkName] = &network.EndpointSettings{ 460 EndpointSettings: &networktypes.EndpointSettings{}, 461 } 462 } 463 464 // Convert any settings added by client in default name to 465 // engine's default network name key 466 if mode.IsDefault() { 467 if nConf, ok := container.NetworkSettings.Networks[mode.NetworkName()]; ok { 468 container.NetworkSettings.Networks[networkName] = nConf 469 delete(container.NetworkSettings.Networks, mode.NetworkName()) 470 } 471 } 472 473 if !mode.IsUserDefined() { 474 return 475 } 476 // Make sure to internally store the per network endpoint config by network name 477 if _, ok := container.NetworkSettings.Networks[networkName]; ok { 478 return 479 } 480 481 if n != nil { 482 if nwConfig, ok := container.NetworkSettings.Networks[n.ID()]; ok { 483 container.NetworkSettings.Networks[networkName] = nwConfig 484 delete(container.NetworkSettings.Networks, n.ID()) 485 return 486 } 487 } 488 } 489 490 func (daemon *Daemon) allocateNetwork(cfg *config.Config, container *container.Container) (retErr error) { 491 if daemon.netController == nil { 492 return nil 493 } 494 495 start := time.Now() 496 497 // Cleanup any stale sandbox left over due to ungraceful daemon shutdown 498 if err := daemon.netController.SandboxDestroy(container.ID); err != nil { 499 log.G(context.TODO()).WithError(err).Errorf("failed to cleanup up stale network sandbox for container %s", container.ID) 500 } 501 502 if container.Config.NetworkDisabled || container.HostConfig.NetworkMode.IsContainer() { 503 return nil 504 } 505 506 updateSettings := false 507 if len(container.NetworkSettings.Networks) == 0 { 508 daemon.updateContainerNetworkSettings(container, nil) 509 updateSettings = true 510 } 511 512 // always connect default network first since only default 513 // network mode support link and we need do some setting 514 // on sandbox initialize for link, but the sandbox only be initialized 515 // on first network connecting. 516 defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() 517 if nConf, ok := container.NetworkSettings.Networks[defaultNetName]; ok { 518 cleanOperationalData(nConf) 519 if err := daemon.connectToNetwork(cfg, container, defaultNetName, nConf, updateSettings); err != nil { 520 return err 521 } 522 } 523 524 // the intermediate map is necessary because "connectToNetwork" modifies "container.NetworkSettings.Networks" 525 networks := make(map[string]*network.EndpointSettings) 526 for n, epConf := range container.NetworkSettings.Networks { 527 if n == defaultNetName { 528 continue 529 } 530 531 networks[n] = epConf 532 } 533 534 for netName, epConf := range networks { 535 cleanOperationalData(epConf) 536 if err := daemon.connectToNetwork(cfg, container, netName, epConf, updateSettings); err != nil { 537 return err 538 } 539 } 540 541 // If the container is not to be connected to any network, 542 // create its network sandbox now if not present 543 if len(networks) == 0 { 544 if _, err := daemon.netController.GetSandbox(container.ID); err != nil { 545 if !errdefs.IsNotFound(err) { 546 return err 547 } 548 549 sbOptions, err := daemon.buildSandboxOptions(cfg, container) 550 if err != nil { 551 return err 552 } 553 sb, err := daemon.netController.NewSandbox(container.ID, sbOptions...) 554 if err != nil { 555 return err 556 } 557 setNetworkSandbox(container, sb) 558 defer func() { 559 if retErr != nil { 560 sb.Delete() 561 } 562 }() 563 } 564 } 565 566 if _, err := container.WriteHostConfig(); err != nil { 567 return err 568 } 569 networkActions.WithValues("allocate").UpdateSince(start) 570 return nil 571 } 572 573 // validateEndpointSettings checks whether the given epConfig is valid. The nw parameter can be nil, in which case it 574 // won't try to check if the endpoint IP addresses are within network's subnets. 575 func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *networktypes.EndpointSettings) error { 576 if epConfig == nil { 577 return nil 578 } 579 580 ipamConfig := &networktypes.EndpointIPAMConfig{} 581 if epConfig.IPAMConfig != nil { 582 ipamConfig = epConfig.IPAMConfig 583 } 584 585 var errs []error 586 587 // TODO(aker): move this into api/types/network/endpoint.go once enableIPOnPredefinedNetwork and 588 // serviceDiscoveryOnDefaultNetwork are removed. 589 if !containertypes.NetworkMode(nwName).IsUserDefined() { 590 hasStaticAddresses := ipamConfig.IPv4Address != "" || ipamConfig.IPv6Address != "" 591 // On Linux, user specified IP address is accepted only by networks with user specified subnets. 592 if hasStaticAddresses && !enableIPOnPredefinedNetwork() { 593 errs = append(errs, runconfig.ErrUnsupportedNetworkAndIP) 594 } 595 if len(epConfig.Aliases) > 0 && !serviceDiscoveryOnDefaultNetwork() { 596 errs = append(errs, runconfig.ErrUnsupportedNetworkAndAlias) 597 } 598 } 599 600 // TODO(aker): add a proper multierror.Append 601 if err := ipamConfig.Validate(); err != nil { 602 errs = append(errs, err.(interface{ Unwrap() []error }).Unwrap()...) 603 } 604 605 if nw != nil { 606 _, _, v4Configs, v6Configs := nw.IpamConfig() 607 608 var nwIPv4Subnets, nwIPv6Subnets []networktypes.NetworkSubnet 609 for _, nwIPAMConfig := range v4Configs { 610 nwIPv4Subnets = append(nwIPv4Subnets, nwIPAMConfig) 611 } 612 for _, nwIPAMConfig := range v6Configs { 613 nwIPv6Subnets = append(nwIPv6Subnets, nwIPAMConfig) 614 } 615 616 // TODO(aker): add a proper multierror.Append 617 if err := ipamConfig.IsInRange(nwIPv4Subnets, nwIPv6Subnets); err != nil { 618 errs = append(errs, err.(interface{ Unwrap() []error }).Unwrap()...) 619 } 620 } 621 622 if epConfig.MacAddress != "" { 623 _, err := net.ParseMAC(epConfig.MacAddress) 624 if err != nil { 625 return fmt.Errorf("invalid MAC address %s", epConfig.MacAddress) 626 } 627 } 628 629 if err := multierror.Join(errs...); err != nil { 630 return fmt.Errorf("invalid endpoint settings:\n%w", err) 631 } 632 633 return nil 634 } 635 636 // cleanOperationalData resets the operational data from the passed endpoint settings 637 func cleanOperationalData(es *network.EndpointSettings) { 638 es.EndpointID = "" 639 es.Gateway = "" 640 es.IPAddress = "" 641 es.IPPrefixLen = 0 642 es.IPv6Gateway = "" 643 es.GlobalIPv6Address = "" 644 es.GlobalIPv6PrefixLen = 0 645 es.MacAddress = "" 646 if es.IPAMOperational { 647 es.IPAMConfig = nil 648 } 649 } 650 651 func (daemon *Daemon) updateNetworkConfig(container *container.Container, n *libnetwork.Network, endpointConfig *networktypes.EndpointSettings, updateSettings bool) error { 652 // Set up DNS names for a user defined network, and for the default 'nat' 653 // network on Windows (IsBridge() returns true for nat). 654 if containertypes.NetworkMode(n.Name()).IsUserDefined() || 655 (serviceDiscoveryOnDefaultNetwork() && containertypes.NetworkMode(n.Name()).IsBridge()) { 656 endpointConfig.DNSNames = buildEndpointDNSNames(container, endpointConfig.Aliases) 657 } 658 659 if err := validateEndpointSettings(n, n.Name(), endpointConfig); err != nil { 660 return errdefs.InvalidParameter(err) 661 } 662 663 if updateSettings { 664 if err := daemon.updateNetworkSettings(container, n, endpointConfig); err != nil { 665 return err 666 } 667 } 668 return nil 669 } 670 671 // buildEndpointDNSNames constructs the list of DNSNames that should be assigned to a given endpoint. The order within 672 // the returned slice is important as the first entry will be used to generate the PTR records (for IPv4 and v6) 673 // associated to this endpoint. 674 func buildEndpointDNSNames(ctr *container.Container, aliases []string) []string { 675 var dnsNames []string 676 677 if ctr.Name != "" { 678 dnsNames = append(dnsNames, strings.TrimPrefix(ctr.Name, "/")) 679 } 680 681 dnsNames = append(dnsNames, aliases...) 682 683 if ctr.ID != "" { 684 dnsNames = append(dnsNames, stringid.TruncateID(ctr.ID)) 685 } 686 687 if ctr.Config.Hostname != "" { 688 dnsNames = append(dnsNames, ctr.Config.Hostname) 689 } 690 691 return sliceutil.Dedup(dnsNames) 692 } 693 694 func (daemon *Daemon) connectToNetwork(cfg *config.Config, container *container.Container, idOrName string, endpointConfig *network.EndpointSettings, updateSettings bool) (retErr error) { 695 start := time.Now() 696 if container.HostConfig.NetworkMode.IsContainer() { 697 return runconfig.ErrConflictSharedNetwork 698 } 699 if cfg.DisableBridge && containertypes.NetworkMode(idOrName).IsBridge() { 700 container.Config.NetworkDisabled = true 701 return nil 702 } 703 if endpointConfig == nil { 704 endpointConfig = &network.EndpointSettings{ 705 EndpointSettings: &networktypes.EndpointSettings{}, 706 } 707 } 708 709 n, nwCfg, err := daemon.findAndAttachNetwork(container, idOrName, endpointConfig.EndpointSettings) 710 if err != nil { 711 return err 712 } 713 if n == nil { 714 return nil 715 } 716 nwName := n.Name() 717 718 if idOrName != container.HostConfig.NetworkMode.NetworkName() { 719 if err := daemon.normalizeNetMode(container); err != nil { 720 return err 721 } 722 } 723 724 endpointConfig.IPAMOperational = false 725 if nwCfg != nil { 726 if epConfig, ok := nwCfg.EndpointsConfig[nwName]; ok { 727 if endpointConfig.IPAMConfig == nil || (endpointConfig.IPAMConfig.IPv4Address == "" && endpointConfig.IPAMConfig.IPv6Address == "" && len(endpointConfig.IPAMConfig.LinkLocalIPs) == 0) { 728 endpointConfig.IPAMOperational = true 729 } 730 731 // copy IPAMConfig and NetworkID from epConfig via AttachNetwork 732 endpointConfig.IPAMConfig = epConfig.IPAMConfig 733 endpointConfig.NetworkID = epConfig.NetworkID 734 } 735 } 736 737 if err := daemon.updateNetworkConfig(container, n, endpointConfig.EndpointSettings, updateSettings); err != nil { 738 return err 739 } 740 741 // TODO(thaJeztah): should this fail early if no sandbox was found? 742 sb, _ := daemon.netController.GetSandbox(container.ID) 743 createOptions, err := buildCreateEndpointOptions(container, n, endpointConfig, sb, ipAddresses(cfg.DNS)) 744 if err != nil { 745 return err 746 } 747 748 endpointName := strings.TrimPrefix(container.Name, "/") 749 ep, err := n.CreateEndpoint(endpointName, createOptions...) 750 if err != nil { 751 return err 752 } 753 defer func() { 754 if retErr != nil { 755 if err := ep.Delete(false); err != nil { 756 log.G(context.TODO()).Warnf("Could not rollback container connection to network %s", idOrName) 757 } 758 } 759 }() 760 container.NetworkSettings.Networks[nwName] = endpointConfig 761 762 delete(container.NetworkSettings.Networks, n.ID()) 763 764 if err := daemon.updateEndpointNetworkSettings(cfg, container, n, ep); err != nil { 765 return err 766 } 767 768 if sb == nil { 769 sbOptions, err := daemon.buildSandboxOptions(cfg, container) 770 if err != nil { 771 return err 772 } 773 sb, err = daemon.netController.NewSandbox(container.ID, sbOptions...) 774 if err != nil { 775 return err 776 } 777 778 setNetworkSandbox(container, sb) 779 } 780 781 joinOptions, err := buildJoinOptions(container.NetworkSettings, n) 782 if err != nil { 783 return err 784 } 785 786 if err := ep.Join(sb, joinOptions...); err != nil { 787 return err 788 } 789 790 if !container.Managed { 791 // add container name/alias to DNS 792 if err := daemon.ActivateContainerServiceBinding(container.Name); err != nil { 793 return fmt.Errorf("Activate container service binding for %s failed: %v", container.Name, err) 794 } 795 } 796 797 if err := updateJoinInfo(container.NetworkSettings, n, ep); err != nil { 798 return fmt.Errorf("Updating join info failed: %v", err) 799 } 800 801 container.NetworkSettings.Ports = getPortMapInfo(sb) 802 803 daemon.LogNetworkEventWithAttributes(n, events.ActionConnect, map[string]string{"container": container.ID}) 804 networkActions.WithValues("connect").UpdateSince(start) 805 return nil 806 } 807 808 func updateJoinInfo(networkSettings *network.Settings, n *libnetwork.Network, ep *libnetwork.Endpoint) error { 809 if ep == nil { 810 return errors.New("invalid enppoint whhile building portmap info") 811 } 812 813 if networkSettings == nil { 814 return errors.New("invalid network settings while building port map info") 815 } 816 817 if len(networkSettings.Ports) == 0 { 818 pm, err := getEndpointPortMapInfo(ep) 819 if err != nil { 820 return err 821 } 822 networkSettings.Ports = pm 823 } 824 825 epInfo := ep.Info() 826 if epInfo == nil { 827 // It is not an error to get an empty endpoint info 828 return nil 829 } 830 if epInfo.Gateway() != nil { 831 networkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String() 832 } 833 if epInfo.GatewayIPv6().To16() != nil { 834 networkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String() 835 } 836 return nil 837 } 838 839 // ForceEndpointDelete deletes an endpoint from a network forcefully 840 func (daemon *Daemon) ForceEndpointDelete(name string, networkName string) error { 841 n, err := daemon.FindNetwork(networkName) 842 if err != nil { 843 return err 844 } 845 846 ep, err := n.EndpointByName(name) 847 if err != nil { 848 return err 849 } 850 return ep.Delete(true) 851 } 852 853 func (daemon *Daemon) disconnectFromNetwork(container *container.Container, n *libnetwork.Network, force bool) error { 854 var ( 855 ep *libnetwork.Endpoint 856 sbox *libnetwork.Sandbox 857 ) 858 n.WalkEndpoints(func(current *libnetwork.Endpoint) bool { 859 epInfo := current.Info() 860 if epInfo == nil { 861 return false 862 } 863 if sb := epInfo.Sandbox(); sb != nil { 864 if sb.ContainerID() == container.ID { 865 ep = current 866 sbox = sb 867 return true 868 } 869 } 870 return false 871 }) 872 873 if ep == nil { 874 if force { 875 var err error 876 ep, err = n.EndpointByName(strings.TrimPrefix(container.Name, "/")) 877 if err != nil { 878 return err 879 } 880 return ep.Delete(force) 881 } 882 return fmt.Errorf("container %s is not connected to network %s", container.ID, n.Name()) 883 } 884 885 if err := ep.Leave(sbox); err != nil { 886 return fmt.Errorf("container %s failed to leave network %s: %v", container.ID, n.Name(), err) 887 } 888 889 container.NetworkSettings.Ports = getPortMapInfo(sbox) 890 891 if err := ep.Delete(false); err != nil { 892 return fmt.Errorf("endpoint delete failed for container %s on network %s: %v", container.ID, n.Name(), err) 893 } 894 895 delete(container.NetworkSettings.Networks, n.Name()) 896 897 daemon.tryDetachContainerFromClusterNetwork(n, container) 898 899 return nil 900 } 901 902 func (daemon *Daemon) tryDetachContainerFromClusterNetwork(network *libnetwork.Network, container *container.Container) { 903 if !container.Managed && daemon.clusterProvider != nil && network.Dynamic() { 904 if err := daemon.clusterProvider.DetachNetwork(network.Name(), container.ID); err != nil { 905 log.G(context.TODO()).WithError(err).Warn("error detaching from network") 906 if err := daemon.clusterProvider.DetachNetwork(network.ID(), container.ID); err != nil { 907 log.G(context.TODO()).WithError(err).Warn("error detaching from network") 908 } 909 } 910 } 911 daemon.LogNetworkEventWithAttributes(network, events.ActionDisconnect, map[string]string{ 912 "container": container.ID, 913 }) 914 } 915 916 // normalizeNetMode checks whether the network mode references a network by a partial ID. In that case, it replaces the 917 // partial ID with the full network ID. 918 // TODO(aker): transform ID into name when the referenced network is one of the predefined. 919 func (daemon *Daemon) normalizeNetMode(container *container.Container) error { 920 if container.HostConfig.NetworkMode.IsUserDefined() { 921 netMode := container.HostConfig.NetworkMode.NetworkName() 922 nw, err := daemon.FindNetwork(netMode) 923 if err != nil { 924 return fmt.Errorf("could not find a network matching network mode %s: %w", netMode, err) 925 } 926 927 if netMode != nw.ID() && netMode != nw.Name() { 928 container.HostConfig.NetworkMode = containertypes.NetworkMode(nw.ID()) 929 } 930 } 931 932 return nil 933 } 934 935 func (daemon *Daemon) initializeNetworking(cfg *config.Config, container *container.Container) error { 936 if container.HostConfig.NetworkMode.IsContainer() { 937 // we need to get the hosts files from the container to join 938 nc, err := daemon.getNetworkedContainer(container.ID, container.HostConfig.NetworkMode.ConnectedContainer()) 939 if err != nil { 940 return err 941 } 942 943 err = daemon.initializeNetworkingPaths(container, nc) 944 if err != nil { 945 return err 946 } 947 948 container.Config.Hostname = nc.Config.Hostname 949 container.Config.Domainname = nc.Config.Domainname 950 return nil 951 } 952 953 if container.HostConfig.NetworkMode.IsHost() && container.Config.Hostname == "" { 954 hn, err := os.Hostname() 955 if err != nil { 956 return err 957 } 958 container.Config.Hostname = hn 959 } 960 961 if err := daemon.allocateNetwork(cfg, container); err != nil { 962 return err 963 } 964 965 return container.BuildHostnameFile() 966 } 967 968 func (daemon *Daemon) getNetworkedContainer(containerID, connectedContainerID string) (*container.Container, error) { 969 nc, err := daemon.GetContainer(connectedContainerID) 970 if err != nil { 971 return nil, err 972 } 973 if containerID == nc.ID { 974 return nil, fmt.Errorf("cannot join own network") 975 } 976 if !nc.IsRunning() { 977 return nil, errdefs.Conflict(fmt.Errorf("cannot join network of a non running container: %s", connectedContainerID)) 978 } 979 if nc.IsRestarting() { 980 return nil, errContainerIsRestarting(connectedContainerID) 981 } 982 return nc, nil 983 } 984 985 func (daemon *Daemon) releaseNetwork(container *container.Container) { 986 start := time.Now() 987 // If live-restore is enabled, the daemon cleans up dead containers when it starts up. In that case, the 988 // netController hasn't been initialized yet and so we can't proceed. 989 // TODO(aker): If we hit this case, the endpoint state won't be cleaned up (ie. no call to cleanOperationalData). 990 if daemon.netController == nil { 991 return 992 } 993 // If the container uses the network namespace of another container, it doesn't own it -- nothing to do here. 994 if container.HostConfig.NetworkMode.IsContainer() { 995 return 996 } 997 if container.NetworkSettings == nil { 998 return 999 } 1000 1001 container.NetworkSettings.Ports = nil 1002 sid := container.NetworkSettings.SandboxID 1003 if sid == "" { 1004 return 1005 } 1006 1007 var networks []*libnetwork.Network 1008 for n, epSettings := range container.NetworkSettings.Networks { 1009 if nw, err := daemon.FindNetwork(getNetworkID(n, epSettings.EndpointSettings)); err == nil { 1010 networks = append(networks, nw) 1011 } 1012 1013 if epSettings.EndpointSettings == nil { 1014 continue 1015 } 1016 1017 cleanOperationalData(epSettings) 1018 } 1019 1020 sb, err := daemon.netController.SandboxByID(sid) 1021 if err != nil { 1022 log.G(context.TODO()).Warnf("error locating sandbox id %s: %v", sid, err) 1023 return 1024 } 1025 1026 if err := sb.Delete(); err != nil { 1027 log.G(context.TODO()).Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err) 1028 } 1029 1030 for _, nw := range networks { 1031 daemon.tryDetachContainerFromClusterNetwork(nw, container) 1032 } 1033 networkActions.WithValues("release").UpdateSince(start) 1034 } 1035 1036 func errRemovalContainer(containerID string) error { 1037 return fmt.Errorf("Container %s is marked for removal and cannot be connected or disconnected to the network", containerID) 1038 } 1039 1040 // ConnectToNetwork connects a container to a network 1041 func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error { 1042 if endpointConfig == nil { 1043 endpointConfig = &networktypes.EndpointSettings{} 1044 } 1045 container.Lock() 1046 defer container.Unlock() 1047 1048 if !container.Running { 1049 if container.RemovalInProgress || container.Dead { 1050 return errRemovalContainer(container.ID) 1051 } 1052 1053 n, err := daemon.FindNetwork(idOrName) 1054 if err == nil && n != nil { 1055 if err := daemon.updateNetworkConfig(container, n, endpointConfig, true); err != nil { 1056 return err 1057 } 1058 } else { 1059 container.NetworkSettings.Networks[idOrName] = &network.EndpointSettings{ 1060 EndpointSettings: endpointConfig, 1061 } 1062 } 1063 } else { 1064 epc := &network.EndpointSettings{ 1065 EndpointSettings: endpointConfig, 1066 } 1067 if err := daemon.connectToNetwork(&daemon.config().Config, container, idOrName, epc, true); err != nil { 1068 return err 1069 } 1070 } 1071 1072 return container.CheckpointTo(daemon.containersReplica) 1073 } 1074 1075 // DisconnectFromNetwork disconnects container from network n. 1076 func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, networkName string, force bool) error { 1077 n, err := daemon.FindNetwork(networkName) 1078 container.Lock() 1079 defer container.Unlock() 1080 1081 if !container.Running || (err != nil && force) { 1082 if container.RemovalInProgress || container.Dead { 1083 return errRemovalContainer(container.ID) 1084 } 1085 // In case networkName is resolved we will use n.Name() 1086 // this will cover the case where network id is passed. 1087 if n != nil { 1088 networkName = n.Name() 1089 } 1090 if _, ok := container.NetworkSettings.Networks[networkName]; !ok { 1091 return fmt.Errorf("container %s is not connected to the network %s", container.ID, networkName) 1092 } 1093 delete(container.NetworkSettings.Networks, networkName) 1094 } else if err == nil { 1095 if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() { 1096 return runconfig.ErrConflictHostNetwork 1097 } 1098 1099 if err := daemon.disconnectFromNetwork(container, n, false); err != nil { 1100 return err 1101 } 1102 } else { 1103 return err 1104 } 1105 1106 if err := container.CheckpointTo(daemon.containersReplica); err != nil { 1107 return err 1108 } 1109 1110 if n != nil { 1111 daemon.LogNetworkEventWithAttributes(n, events.ActionDisconnect, map[string]string{ 1112 "container": container.ID, 1113 }) 1114 } 1115 1116 return nil 1117 } 1118 1119 // ActivateContainerServiceBinding puts this container into load balancer active rotation and DNS response 1120 func (daemon *Daemon) ActivateContainerServiceBinding(containerName string) error { 1121 ctr, err := daemon.GetContainer(containerName) 1122 if err != nil { 1123 return err 1124 } 1125 sb, err := daemon.netController.GetSandbox(ctr.ID) 1126 if err != nil { 1127 return fmt.Errorf("failed to activate service binding for container %s: %w", containerName, err) 1128 } 1129 return sb.EnableService() 1130 } 1131 1132 // DeactivateContainerServiceBinding removes this container from load balancer active rotation, and DNS response 1133 func (daemon *Daemon) DeactivateContainerServiceBinding(containerName string) error { 1134 ctr, err := daemon.GetContainer(containerName) 1135 if err != nil { 1136 return err 1137 } 1138 sb, err := daemon.netController.GetSandbox(ctr.ID) 1139 if err != nil { 1140 // If the network sandbox is not found, then there is nothing to deactivate 1141 log.G(context.TODO()).WithError(err).Debugf("Could not find network sandbox for container %s on service binding deactivation request", containerName) 1142 return nil 1143 } 1144 return sb.DisableService() 1145 } 1146 1147 func getNetworkID(name string, endpointSettings *networktypes.EndpointSettings) string { 1148 // We only want to prefer NetworkID for user defined networks. 1149 // For systems like bridge, none, etc. the name is preferred (otherwise restart may cause issues) 1150 if containertypes.NetworkMode(name).IsUserDefined() && endpointSettings != nil && endpointSettings.NetworkID != "" { 1151 return endpointSettings.NetworkID 1152 } 1153 return name 1154 } 1155 1156 // setNetworkSandbox updates the sandbox ID and Key. 1157 func setNetworkSandbox(c *container.Container, sb *libnetwork.Sandbox) { 1158 c.NetworkSettings.SandboxID = sb.ID() 1159 c.NetworkSettings.SandboxKey = sb.Key() 1160 }