github.com/moby/docker@v26.1.3+incompatible/libnetwork/sandbox_linux.go (about)

     1  package libnetwork
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"time"
     8  
     9  	"github.com/containerd/log"
    10  	"github.com/docker/docker/libnetwork/netutils"
    11  	"github.com/docker/docker/libnetwork/osl"
    12  	"github.com/docker/docker/libnetwork/types"
    13  )
    14  
    15  // Linux-specific container configuration flags.
    16  type containerConfigOS struct{} //nolint:nolintlint,unused // only populated on windows
    17  
    18  func releaseOSSboxResources(ns *osl.Namespace, ep *Endpoint) {
    19  	for _, i := range ns.Interfaces() {
    20  		// Only remove the interfaces owned by this endpoint from the sandbox.
    21  		if ep.hasInterface(i.SrcName()) {
    22  			if err := i.Remove(); err != nil {
    23  				log.G(context.TODO()).Debugf("Remove interface %s failed: %v", i.SrcName(), err)
    24  			}
    25  		}
    26  	}
    27  
    28  	ep.mu.Lock()
    29  	joinInfo := ep.joinInfo
    30  	vip := ep.virtualIP
    31  	lbModeIsDSR := ep.network.loadBalancerMode == loadBalancerModeDSR
    32  	ep.mu.Unlock()
    33  
    34  	if len(vip) > 0 && lbModeIsDSR {
    35  		ipNet := &net.IPNet{IP: vip, Mask: net.CIDRMask(32, 32)}
    36  		if err := ns.RemoveAliasIP(ns.GetLoopbackIfaceName(), ipNet); err != nil {
    37  			log.G(context.TODO()).WithError(err).Debugf("failed to remove virtual ip %v to loopback", ipNet)
    38  		}
    39  	}
    40  
    41  	if joinInfo == nil {
    42  		return
    43  	}
    44  
    45  	// Remove non-interface routes.
    46  	for _, r := range joinInfo.StaticRoutes {
    47  		if err := ns.RemoveStaticRoute(r); err != nil {
    48  			log.G(context.TODO()).Debugf("Remove route failed: %v", err)
    49  		}
    50  	}
    51  }
    52  
    53  // Statistics retrieves the interfaces' statistics for the sandbox.
    54  func (sb *Sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) {
    55  	m := make(map[string]*types.InterfaceStatistics)
    56  
    57  	sb.mu.Lock()
    58  	osb := sb.osSbox
    59  	sb.mu.Unlock()
    60  	if osb == nil {
    61  		return m, nil
    62  	}
    63  
    64  	var err error
    65  	for _, i := range osb.Interfaces() {
    66  		if m[i.DstName()], err = i.Statistics(); err != nil {
    67  			return m, err
    68  		}
    69  	}
    70  
    71  	return m, nil
    72  }
    73  
    74  func (sb *Sandbox) updateGateway(ep *Endpoint) error {
    75  	sb.mu.Lock()
    76  	osSbox := sb.osSbox
    77  	sb.mu.Unlock()
    78  	if osSbox == nil {
    79  		return nil
    80  	}
    81  	osSbox.UnsetGateway()     //nolint:errcheck
    82  	osSbox.UnsetGatewayIPv6() //nolint:errcheck
    83  
    84  	if ep == nil {
    85  		return nil
    86  	}
    87  
    88  	ep.mu.Lock()
    89  	joinInfo := ep.joinInfo
    90  	ep.mu.Unlock()
    91  
    92  	if err := osSbox.SetGateway(joinInfo.gw); err != nil {
    93  		return fmt.Errorf("failed to set gateway while updating gateway: %v", err)
    94  	}
    95  
    96  	if err := osSbox.SetGatewayIPv6(joinInfo.gw6); err != nil {
    97  		return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err)
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func (sb *Sandbox) ExecFunc(f func()) error {
   104  	sb.mu.Lock()
   105  	osSbox := sb.osSbox
   106  	sb.mu.Unlock()
   107  	if osSbox != nil {
   108  		return osSbox.InvokeFunc(f)
   109  	}
   110  	return fmt.Errorf("osl sandbox unavailable in ExecFunc for %v", sb.ContainerID())
   111  }
   112  
   113  // SetKey updates the Sandbox Key.
   114  func (sb *Sandbox) SetKey(basePath string) error {
   115  	start := time.Now()
   116  	defer func() {
   117  		log.G(context.TODO()).Debugf("sandbox set key processing took %s for container %s", time.Since(start), sb.ContainerID())
   118  	}()
   119  
   120  	if basePath == "" {
   121  		return types.InvalidParameterErrorf("invalid sandbox key")
   122  	}
   123  
   124  	sb.mu.Lock()
   125  	if sb.inDelete {
   126  		sb.mu.Unlock()
   127  		return types.ForbiddenErrorf("failed to SetKey: sandbox %q delete in progress", sb.id)
   128  	}
   129  	oldosSbox := sb.osSbox
   130  	sb.mu.Unlock()
   131  
   132  	if oldosSbox != nil {
   133  		// If we already have an OS sandbox, release the network resources from that
   134  		// and destroy the OS snab. We are moving into a new home further down. Note that none
   135  		// of the network resources gets destroyed during the move.
   136  		if err := sb.releaseOSSbox(); err != nil {
   137  			log.G(context.TODO()).WithError(err).Error("Error destroying os sandbox")
   138  		}
   139  	}
   140  
   141  	osSbox, err := osl.GetSandboxForExternalKey(basePath, sb.Key())
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	sb.mu.Lock()
   147  	sb.osSbox = osSbox
   148  	sb.mu.Unlock()
   149  
   150  	// If the resolver was setup before stop it and set it up in the
   151  	// new osl sandbox.
   152  	if oldosSbox != nil && sb.resolver != nil {
   153  		sb.resolver.Stop()
   154  
   155  		if err := sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0)); err == nil {
   156  			if err := sb.resolver.Start(); err != nil {
   157  				log.G(context.TODO()).Errorf("Resolver Start failed for container %s, %q", sb.ContainerID(), err)
   158  			}
   159  		} else {
   160  			log.G(context.TODO()).Errorf("Resolver Setup Function failed for container %s, %q", sb.ContainerID(), err)
   161  		}
   162  	}
   163  
   164  	// Set up hosts and resolv.conf files. IPv6 support in the container can't be
   165  	// determined yet, as sysctls haven't been applied by the runtime. Calling
   166  	// FinishInit after the container task has been created, when sysctls have been
   167  	// applied will regenerate these files.
   168  	if err := sb.finishInitDNS(); err != nil {
   169  		return err
   170  	}
   171  
   172  	for _, ep := range sb.Endpoints() {
   173  		if err = sb.populateNetworkResources(ep); err != nil {
   174  			return err
   175  		}
   176  	}
   177  
   178  	return nil
   179  }
   180  
   181  // FinishConfig completes Sandbox configuration. If called after the container task has been
   182  // created, and sysctl settings applied, the configuration will be based on the container's
   183  // IPv6 support.
   184  func (sb *Sandbox) FinishConfig() error {
   185  	if sb.config.useDefaultSandBox {
   186  		return nil
   187  	}
   188  
   189  	sb.mu.Lock()
   190  	osSbox := sb.osSbox
   191  	sb.mu.Unlock()
   192  	if osSbox == nil {
   193  		return nil
   194  	}
   195  
   196  	// If sysctl changes have been made, IPv6 may have been enabled/disabled since last checked.
   197  	osSbox.RefreshIPv6LoEnabled()
   198  
   199  	return sb.finishInitDNS()
   200  }
   201  
   202  // IPv6 support can always be determined for host networking. For other network
   203  // types it can only be determined once there's a container namespace to probe,
   204  // return ok=false in that case.
   205  func (sb *Sandbox) ipv6Enabled() (enabled, ok bool) {
   206  	// For host networking, IPv6 support depends on the host.
   207  	if sb.config.useDefaultSandBox {
   208  		return netutils.IsV6Listenable(), true
   209  	}
   210  
   211  	// For other network types, look at whether the container's loopback interface has an IPv6 address.
   212  	sb.mu.Lock()
   213  	osSbox := sb.osSbox
   214  	sb.mu.Unlock()
   215  
   216  	if osSbox == nil {
   217  		return false, false
   218  	}
   219  	return osSbox.IPv6LoEnabled(), true
   220  }
   221  
   222  func (sb *Sandbox) releaseOSSbox() error {
   223  	sb.mu.Lock()
   224  	osSbox := sb.osSbox
   225  	sb.osSbox = nil
   226  	sb.mu.Unlock()
   227  
   228  	if osSbox == nil {
   229  		return nil
   230  	}
   231  
   232  	for _, ep := range sb.Endpoints() {
   233  		releaseOSSboxResources(osSbox, ep)
   234  	}
   235  
   236  	return osSbox.Destroy()
   237  }
   238  
   239  func (sb *Sandbox) restoreOslSandbox() error {
   240  	var routes []*types.StaticRoute
   241  
   242  	// restore osl sandbox
   243  	interfaces := make(map[osl.Iface][]osl.IfaceOption)
   244  	for _, ep := range sb.endpoints {
   245  		ep.mu.Lock()
   246  		joinInfo := ep.joinInfo
   247  		i := ep.iface
   248  		ep.mu.Unlock()
   249  
   250  		if i == nil {
   251  			log.G(context.TODO()).Errorf("error restoring endpoint %s for container %s", ep.Name(), sb.ContainerID())
   252  			continue
   253  		}
   254  
   255  		ifaceOptions := []osl.IfaceOption{
   256  			osl.WithIPv4Address(i.addr),
   257  			osl.WithRoutes(i.routes),
   258  		}
   259  		if i.addrv6 != nil && i.addrv6.IP.To16() != nil {
   260  			ifaceOptions = append(ifaceOptions, osl.WithIPv6Address(i.addrv6))
   261  		}
   262  		if i.mac != nil {
   263  			ifaceOptions = append(ifaceOptions, osl.WithMACAddress(i.mac))
   264  		}
   265  		if len(i.llAddrs) != 0 {
   266  			ifaceOptions = append(ifaceOptions, osl.WithLinkLocalAddresses(i.llAddrs))
   267  		}
   268  		interfaces[osl.Iface{SrcName: i.srcName, DstPrefix: i.dstPrefix}] = ifaceOptions
   269  		if joinInfo != nil {
   270  			routes = append(routes, joinInfo.StaticRoutes...)
   271  		}
   272  		if ep.needResolver() {
   273  			sb.startResolver(true)
   274  		}
   275  	}
   276  
   277  	gwep := sb.getGatewayEndpoint()
   278  	if gwep == nil {
   279  		return nil
   280  	}
   281  
   282  	// restore osl sandbox
   283  	return sb.osSbox.Restore(interfaces, routes, gwep.joinInfo.gw, gwep.joinInfo.gw6)
   284  }
   285  
   286  func (sb *Sandbox) populateNetworkResources(ep *Endpoint) error {
   287  	sb.mu.Lock()
   288  	if sb.osSbox == nil {
   289  		sb.mu.Unlock()
   290  		return nil
   291  	}
   292  	inDelete := sb.inDelete
   293  	sb.mu.Unlock()
   294  
   295  	ep.mu.Lock()
   296  	joinInfo := ep.joinInfo
   297  	i := ep.iface
   298  	lbModeIsDSR := ep.network.loadBalancerMode == loadBalancerModeDSR
   299  	ep.mu.Unlock()
   300  
   301  	if ep.needResolver() {
   302  		sb.startResolver(false)
   303  	}
   304  
   305  	if i != nil && i.srcName != "" {
   306  		var ifaceOptions []osl.IfaceOption
   307  
   308  		ifaceOptions = append(ifaceOptions, osl.WithIPv4Address(i.addr), osl.WithRoutes(i.routes))
   309  		if i.addrv6 != nil && i.addrv6.IP.To16() != nil {
   310  			ifaceOptions = append(ifaceOptions, osl.WithIPv6Address(i.addrv6))
   311  		}
   312  		if len(i.llAddrs) != 0 {
   313  			ifaceOptions = append(ifaceOptions, osl.WithLinkLocalAddresses(i.llAddrs))
   314  		}
   315  		if i.mac != nil {
   316  			ifaceOptions = append(ifaceOptions, osl.WithMACAddress(i.mac))
   317  		}
   318  
   319  		if err := sb.osSbox.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil {
   320  			return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err)
   321  		}
   322  
   323  		if len(ep.virtualIP) > 0 && lbModeIsDSR {
   324  			if sb.loadBalancerNID == "" {
   325  				if err := sb.osSbox.DisableARPForVIP(i.srcName); err != nil {
   326  					return fmt.Errorf("failed disable ARP for VIP: %v", err)
   327  				}
   328  			}
   329  			ipNet := &net.IPNet{IP: ep.virtualIP, Mask: net.CIDRMask(32, 32)}
   330  			if err := sb.osSbox.AddAliasIP(sb.osSbox.GetLoopbackIfaceName(), ipNet); err != nil {
   331  				return fmt.Errorf("failed to add virtual ip %v to loopback: %v", ipNet, err)
   332  			}
   333  		}
   334  	}
   335  
   336  	if joinInfo != nil {
   337  		// Set up non-interface routes.
   338  		for _, r := range joinInfo.StaticRoutes {
   339  			if err := sb.osSbox.AddStaticRoute(r); err != nil {
   340  				return fmt.Errorf("failed to add static route %s: %v", r.Destination.String(), err)
   341  			}
   342  		}
   343  	}
   344  
   345  	if ep == sb.getGatewayEndpoint() {
   346  		if err := sb.updateGateway(ep); err != nil {
   347  			return err
   348  		}
   349  	}
   350  
   351  	// Make sure to add the endpoint to the populated endpoint set
   352  	// before populating loadbalancers.
   353  	sb.mu.Lock()
   354  	sb.populatedEndpoints[ep.ID()] = struct{}{}
   355  	sb.mu.Unlock()
   356  
   357  	// Populate load balancer only after updating all the other
   358  	// information including gateway and other routes so that
   359  	// loadbalancers are populated all the network state is in
   360  	// place in the sandbox.
   361  	sb.populateLoadBalancers(ep)
   362  
   363  	// Only update the store if we did not come here as part of
   364  	// sandbox delete. If we came here as part of delete then do
   365  	// not bother updating the store. The sandbox object will be
   366  	// deleted anyway
   367  	if !inDelete {
   368  		return sb.storeUpdate()
   369  	}
   370  
   371  	return nil
   372  }