github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/api/types/network/endpoint.go (about)

     1  package network
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  
     8  	"github.com/docker/docker/internal/multierror"
     9  )
    10  
    11  // EndpointSettings stores the network endpoint details
    12  type EndpointSettings struct {
    13  	// Configurations
    14  	IPAMConfig *EndpointIPAMConfig
    15  	Links      []string
    16  	Aliases    []string // Aliases holds the list of extra, user-specified DNS names for this endpoint.
    17  	// MacAddress may be used to specify a MAC address when the container is created.
    18  	// Once the container is running, it becomes operational data (it may contain a
    19  	// generated address).
    20  	MacAddress string
    21  	// Operational data
    22  	NetworkID           string
    23  	EndpointID          string
    24  	Gateway             string
    25  	IPAddress           string
    26  	IPPrefixLen         int
    27  	IPv6Gateway         string
    28  	GlobalIPv6Address   string
    29  	GlobalIPv6PrefixLen int
    30  	DriverOpts          map[string]string
    31  	// DNSNames holds all the (non fully qualified) DNS names associated to this endpoint. First entry is used to
    32  	// generate PTR records.
    33  	DNSNames []string
    34  }
    35  
    36  // Copy makes a deep copy of `EndpointSettings`
    37  func (es *EndpointSettings) Copy() *EndpointSettings {
    38  	epCopy := *es
    39  	if es.IPAMConfig != nil {
    40  		epCopy.IPAMConfig = es.IPAMConfig.Copy()
    41  	}
    42  
    43  	if es.Links != nil {
    44  		links := make([]string, 0, len(es.Links))
    45  		epCopy.Links = append(links, es.Links...)
    46  	}
    47  
    48  	if es.Aliases != nil {
    49  		aliases := make([]string, 0, len(es.Aliases))
    50  		epCopy.Aliases = append(aliases, es.Aliases...)
    51  	}
    52  
    53  	if len(es.DNSNames) > 0 {
    54  		epCopy.DNSNames = make([]string, len(es.DNSNames))
    55  		copy(epCopy.DNSNames, es.DNSNames)
    56  	}
    57  
    58  	return &epCopy
    59  }
    60  
    61  // EndpointIPAMConfig represents IPAM configurations for the endpoint
    62  type EndpointIPAMConfig struct {
    63  	IPv4Address  string   `json:",omitempty"`
    64  	IPv6Address  string   `json:",omitempty"`
    65  	LinkLocalIPs []string `json:",omitempty"`
    66  }
    67  
    68  // Copy makes a copy of the endpoint ipam config
    69  func (cfg *EndpointIPAMConfig) Copy() *EndpointIPAMConfig {
    70  	cfgCopy := *cfg
    71  	cfgCopy.LinkLocalIPs = make([]string, 0, len(cfg.LinkLocalIPs))
    72  	cfgCopy.LinkLocalIPs = append(cfgCopy.LinkLocalIPs, cfg.LinkLocalIPs...)
    73  	return &cfgCopy
    74  }
    75  
    76  // NetworkSubnet describes a user-defined subnet for a specific network. It's only used to validate if an
    77  // EndpointIPAMConfig is valid for a specific network.
    78  type NetworkSubnet interface {
    79  	// Contains checks whether the NetworkSubnet contains [addr].
    80  	Contains(addr net.IP) bool
    81  	// IsStatic checks whether the subnet was statically allocated (ie. user-defined).
    82  	IsStatic() bool
    83  }
    84  
    85  // IsInRange checks whether static IP addresses are valid in a specific network.
    86  func (cfg *EndpointIPAMConfig) IsInRange(v4Subnets []NetworkSubnet, v6Subnets []NetworkSubnet) error {
    87  	var errs []error
    88  
    89  	if err := validateEndpointIPAddress(cfg.IPv4Address, v4Subnets); err != nil {
    90  		errs = append(errs, err)
    91  	}
    92  	if err := validateEndpointIPAddress(cfg.IPv6Address, v6Subnets); err != nil {
    93  		errs = append(errs, err)
    94  	}
    95  
    96  	return multierror.Join(errs...)
    97  }
    98  
    99  func validateEndpointIPAddress(epAddr string, ipamSubnets []NetworkSubnet) error {
   100  	if epAddr == "" {
   101  		return nil
   102  	}
   103  
   104  	var staticSubnet bool
   105  	parsedAddr := net.ParseIP(epAddr)
   106  	for _, subnet := range ipamSubnets {
   107  		if subnet.IsStatic() {
   108  			staticSubnet = true
   109  			if subnet.Contains(parsedAddr) {
   110  				return nil
   111  			}
   112  		}
   113  	}
   114  
   115  	if staticSubnet {
   116  		return fmt.Errorf("no configured subnet or ip-range contain the IP address %s", epAddr)
   117  	}
   118  
   119  	return errors.New("user specified IP address is supported only when connecting to networks with user configured subnets")
   120  }
   121  
   122  // Validate checks whether cfg is valid.
   123  func (cfg *EndpointIPAMConfig) Validate() error {
   124  	if cfg == nil {
   125  		return nil
   126  	}
   127  
   128  	var errs []error
   129  
   130  	if cfg.IPv4Address != "" {
   131  		if addr := net.ParseIP(cfg.IPv4Address); addr == nil || addr.To4() == nil || addr.IsUnspecified() {
   132  			errs = append(errs, fmt.Errorf("invalid IPv4 address: %s", cfg.IPv4Address))
   133  		}
   134  	}
   135  	if cfg.IPv6Address != "" {
   136  		if addr := net.ParseIP(cfg.IPv6Address); addr == nil || addr.To4() != nil || addr.IsUnspecified() {
   137  			errs = append(errs, fmt.Errorf("invalid IPv6 address: %s", cfg.IPv6Address))
   138  		}
   139  	}
   140  	for _, addr := range cfg.LinkLocalIPs {
   141  		if parsed := net.ParseIP(addr); parsed == nil || parsed.IsUnspecified() {
   142  			errs = append(errs, fmt.Errorf("invalid link-local IP address: %s", addr))
   143  		}
   144  	}
   145  
   146  	return multierror.Join(errs...)
   147  }