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