package libnetwork import ( "context" "fmt" "net" "time" "github.com/containerd/log" "github.com/docker/docker/libnetwork/netutils" "github.com/docker/docker/libnetwork/osl" "github.com/docker/docker/libnetwork/types" ) // Linux-specific container configuration flags. type containerConfigOS struct{} //nolint:nolintlint,unused // only populated on windows func releaseOSSboxResources(ns *osl.Namespace, ep *Endpoint) { for _, i := range ns.Interfaces() { // Only remove the interfaces owned by this endpoint from the sandbox. if ep.hasInterface(i.SrcName()) { if err := i.Remove(); err != nil { log.G(context.TODO()).Debugf("Remove interface %s failed: %v", i.SrcName(), err) } } } ep.mu.Lock() joinInfo := ep.joinInfo vip := ep.virtualIP lbModeIsDSR := ep.network.loadBalancerMode == loadBalancerModeDSR ep.mu.Unlock() if len(vip) > 0 && lbModeIsDSR { ipNet := &net.IPNet{IP: vip, Mask: net.CIDRMask(32, 32)} if err := ns.RemoveAliasIP(ns.GetLoopbackIfaceName(), ipNet); err != nil { log.G(context.TODO()).WithError(err).Debugf("failed to remove virtual ip %v to loopback", ipNet) } } if joinInfo == nil { return } // Remove non-interface routes. for _, r := range joinInfo.StaticRoutes { if err := ns.RemoveStaticRoute(r); err != nil { log.G(context.TODO()).Debugf("Remove route failed: %v", err) } } } // Statistics retrieves the interfaces' statistics for the sandbox. func (sb *Sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) { m := make(map[string]*types.InterfaceStatistics) sb.mu.Lock() osb := sb.osSbox sb.mu.Unlock() if osb == nil { return m, nil } var err error for _, i := range osb.Interfaces() { if m[i.DstName()], err = i.Statistics(); err != nil { return m, err } } return m, nil } func (sb *Sandbox) updateGateway(ep *Endpoint) error { sb.mu.Lock() osSbox := sb.osSbox sb.mu.Unlock() if osSbox == nil { return nil } osSbox.UnsetGateway() //nolint:errcheck osSbox.UnsetGatewayIPv6() //nolint:errcheck if ep == nil { return nil } ep.mu.Lock() joinInfo := ep.joinInfo ep.mu.Unlock() if err := osSbox.SetGateway(joinInfo.gw); err != nil { return fmt.Errorf("failed to set gateway while updating gateway: %v", err) } if err := osSbox.SetGatewayIPv6(joinInfo.gw6); err != nil { return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err) } return nil } func (sb *Sandbox) ExecFunc(f func()) error { sb.mu.Lock() osSbox := sb.osSbox sb.mu.Unlock() if osSbox != nil { return osSbox.InvokeFunc(f) } return fmt.Errorf("osl sandbox unavailable in ExecFunc for %v", sb.ContainerID()) } // SetKey updates the Sandbox Key. func (sb *Sandbox) SetKey(basePath string) error { start := time.Now() defer func() { log.G(context.TODO()).Debugf("sandbox set key processing took %s for container %s", time.Since(start), sb.ContainerID()) }() if basePath == "" { return types.InvalidParameterErrorf("invalid sandbox key") } sb.mu.Lock() if sb.inDelete { sb.mu.Unlock() return types.ForbiddenErrorf("failed to SetKey: sandbox %q delete in progress", sb.id) } oldosSbox := sb.osSbox sb.mu.Unlock() if oldosSbox != nil { // If we already have an OS sandbox, release the network resources from that // and destroy the OS snab. We are moving into a new home further down. Note that none // of the network resources gets destroyed during the move. if err := sb.releaseOSSbox(); err != nil { log.G(context.TODO()).WithError(err).Error("Error destroying os sandbox") } } osSbox, err := osl.GetSandboxForExternalKey(basePath, sb.Key()) if err != nil { return err } sb.mu.Lock() sb.osSbox = osSbox sb.mu.Unlock() // If the resolver was setup before stop it and set it up in the // new osl sandbox. if oldosSbox != nil && sb.resolver != nil { sb.resolver.Stop() if err := sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0)); err == nil { if err := sb.resolver.Start(); err != nil { log.G(context.TODO()).Errorf("Resolver Start failed for container %s, %q", sb.ContainerID(), err) } } else { log.G(context.TODO()).Errorf("Resolver Setup Function failed for container %s, %q", sb.ContainerID(), err) } } // Set up hosts and resolv.conf files. IPv6 support in the container can't be // determined yet, as sysctls haven't been applied by the runtime. Calling // FinishInit after the container task has been created, when sysctls have been // applied will regenerate these files. if err := sb.finishInitDNS(); err != nil { return err } for _, ep := range sb.Endpoints() { if err = sb.populateNetworkResources(ep); err != nil { return err } } return nil } // FinishConfig completes Sandbox configuration. If called after the container task has been // created, and sysctl settings applied, the configuration will be based on the container's // IPv6 support. func (sb *Sandbox) FinishConfig() error { if sb.config.useDefaultSandBox { return nil } sb.mu.Lock() osSbox := sb.osSbox sb.mu.Unlock() if osSbox == nil { return nil } // If sysctl changes have been made, IPv6 may have been enabled/disabled since last checked. osSbox.RefreshIPv6LoEnabled() return sb.finishInitDNS() } // IPv6 support can always be determined for host networking. For other network // types it can only be determined once there's a container namespace to probe, // return ok=false in that case. func (sb *Sandbox) ipv6Enabled() (enabled, ok bool) { // For host networking, IPv6 support depends on the host. if sb.config.useDefaultSandBox { return netutils.IsV6Listenable(), true } // For other network types, look at whether the container's loopback interface has an IPv6 address. sb.mu.Lock() osSbox := sb.osSbox sb.mu.Unlock() if osSbox == nil { return false, false } return osSbox.IPv6LoEnabled(), true } func (sb *Sandbox) releaseOSSbox() error { sb.mu.Lock() osSbox := sb.osSbox sb.osSbox = nil sb.mu.Unlock() if osSbox == nil { return nil } for _, ep := range sb.Endpoints() { releaseOSSboxResources(osSbox, ep) } return osSbox.Destroy() } func (sb *Sandbox) restoreOslSandbox() error { var routes []*types.StaticRoute // restore osl sandbox interfaces := make(map[osl.Iface][]osl.IfaceOption) for _, ep := range sb.endpoints { ep.mu.Lock() joinInfo := ep.joinInfo i := ep.iface ep.mu.Unlock() if i == nil { log.G(context.TODO()).Errorf("error restoring endpoint %s for container %s", ep.Name(), sb.ContainerID()) continue } ifaceOptions := []osl.IfaceOption{ osl.WithIPv4Address(i.addr), osl.WithRoutes(i.routes), } if i.addrv6 != nil && i.addrv6.IP.To16() != nil { ifaceOptions = append(ifaceOptions, osl.WithIPv6Address(i.addrv6)) } if i.mac != nil { ifaceOptions = append(ifaceOptions, osl.WithMACAddress(i.mac)) } if len(i.llAddrs) != 0 { ifaceOptions = append(ifaceOptions, osl.WithLinkLocalAddresses(i.llAddrs)) } interfaces[osl.Iface{SrcName: i.srcName, DstPrefix: i.dstPrefix}] = ifaceOptions if joinInfo != nil { routes = append(routes, joinInfo.StaticRoutes...) } if ep.needResolver() { sb.startResolver(true) } } gwep := sb.getGatewayEndpoint() if gwep == nil { return nil } // restore osl sandbox return sb.osSbox.Restore(interfaces, routes, gwep.joinInfo.gw, gwep.joinInfo.gw6) } func (sb *Sandbox) populateNetworkResources(ep *Endpoint) error { sb.mu.Lock() if sb.osSbox == nil { sb.mu.Unlock() return nil } inDelete := sb.inDelete sb.mu.Unlock() ep.mu.Lock() joinInfo := ep.joinInfo i := ep.iface lbModeIsDSR := ep.network.loadBalancerMode == loadBalancerModeDSR ep.mu.Unlock() if ep.needResolver() { sb.startResolver(false) } if i != nil && i.srcName != "" { var ifaceOptions []osl.IfaceOption ifaceOptions = append(ifaceOptions, osl.WithIPv4Address(i.addr), osl.WithRoutes(i.routes)) if i.addrv6 != nil && i.addrv6.IP.To16() != nil { ifaceOptions = append(ifaceOptions, osl.WithIPv6Address(i.addrv6)) } if len(i.llAddrs) != 0 { ifaceOptions = append(ifaceOptions, osl.WithLinkLocalAddresses(i.llAddrs)) } if i.mac != nil { ifaceOptions = append(ifaceOptions, osl.WithMACAddress(i.mac)) } if err := sb.osSbox.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil { return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err) } if len(ep.virtualIP) > 0 && lbModeIsDSR { if sb.loadBalancerNID == "" { if err := sb.osSbox.DisableARPForVIP(i.srcName); err != nil { return fmt.Errorf("failed disable ARP for VIP: %v", err) } } ipNet := &net.IPNet{IP: ep.virtualIP, Mask: net.CIDRMask(32, 32)} if err := sb.osSbox.AddAliasIP(sb.osSbox.GetLoopbackIfaceName(), ipNet); err != nil { return fmt.Errorf("failed to add virtual ip %v to loopback: %v", ipNet, err) } } } if joinInfo != nil { // Set up non-interface routes. for _, r := range joinInfo.StaticRoutes { if err := sb.osSbox.AddStaticRoute(r); err != nil { return fmt.Errorf("failed to add static route %s: %v", r.Destination.String(), err) } } } if ep == sb.getGatewayEndpoint() { if err := sb.updateGateway(ep); err != nil { return err } } // Make sure to add the endpoint to the populated endpoint set // before populating loadbalancers. sb.mu.Lock() sb.populatedEndpoints[ep.ID()] = struct{}{} sb.mu.Unlock() // Populate load balancer only after updating all the other // information including gateway and other routes so that // loadbalancers are populated all the network state is in // place in the sandbox. sb.populateLoadBalancers(ep) // Only update the store if we did not come here as part of // sandbox delete. If we came here as part of delete then do // not bother updating the store. The sandbox object will be // deleted anyway if !inDelete { return sb.storeUpdate() } return nil }