github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/libnetwork/portallocator/portallocator.go (about)

     1  package portallocator
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"sync"
     8  )
     9  
    10  const (
    11  	// DefaultPortRangeStart indicates the first port in port range
    12  	DefaultPortRangeStart = 49153
    13  	// DefaultPortRangeEnd indicates the last port in port range
    14  	DefaultPortRangeEnd = 65535
    15  )
    16  
    17  type ipMapping map[string]protoMap
    18  
    19  var (
    20  	// ErrAllPortsAllocated is returned when no more ports are available
    21  	ErrAllPortsAllocated = errors.New("all ports are allocated")
    22  	// ErrUnknownProtocol is returned when an unknown protocol was specified
    23  	ErrUnknownProtocol = errors.New("unknown protocol")
    24  	defaultIP          = net.ParseIP("0.0.0.0")
    25  	once               sync.Once
    26  	instance           *PortAllocator
    27  	createInstance     = func() { instance = newInstance() }
    28  )
    29  
    30  // ErrPortAlreadyAllocated is the returned error information when a requested port is already being used
    31  type ErrPortAlreadyAllocated struct {
    32  	ip   string
    33  	port int
    34  }
    35  
    36  func newErrPortAlreadyAllocated(ip string, port int) ErrPortAlreadyAllocated {
    37  	return ErrPortAlreadyAllocated{
    38  		ip:   ip,
    39  		port: port,
    40  	}
    41  }
    42  
    43  // IP returns the address to which the used port is associated
    44  func (e ErrPortAlreadyAllocated) IP() string {
    45  	return e.ip
    46  }
    47  
    48  // Port returns the value of the already used port
    49  func (e ErrPortAlreadyAllocated) Port() int {
    50  	return e.port
    51  }
    52  
    53  // IPPort returns the address and the port in the form ip:port
    54  func (e ErrPortAlreadyAllocated) IPPort() string {
    55  	return fmt.Sprintf("%s:%d", e.ip, e.port)
    56  }
    57  
    58  // Error is the implementation of error.Error interface
    59  func (e ErrPortAlreadyAllocated) Error() string {
    60  	return fmt.Sprintf("Bind for %s:%d failed: port is already allocated", e.ip, e.port)
    61  }
    62  
    63  type (
    64  	// PortAllocator manages the transport ports database
    65  	PortAllocator struct {
    66  		mutex sync.Mutex
    67  		ipMap ipMapping
    68  		Begin int
    69  		End   int
    70  	}
    71  	portRange struct {
    72  		begin int
    73  		end   int
    74  		last  int
    75  	}
    76  	portMap struct {
    77  		p            map[int]struct{}
    78  		defaultRange string
    79  		portRanges   map[string]*portRange
    80  	}
    81  	protoMap map[string]*portMap
    82  )
    83  
    84  // Get returns the default instance of PortAllocator
    85  func Get() *PortAllocator {
    86  	// Port Allocator is a singleton
    87  	// Note: Long term solution will be each PortAllocator will have access to
    88  	// the OS so that it can have up to date view of the OS port allocation.
    89  	// When this happens singleton behavior will be removed. Clients do not
    90  	// need to worry about this, they will not see a change in behavior.
    91  	once.Do(createInstance)
    92  	return instance
    93  }
    94  
    95  func newInstance() *PortAllocator {
    96  	start, end, err := getDynamicPortRange()
    97  	if err != nil {
    98  		start, end = DefaultPortRangeStart, DefaultPortRangeEnd
    99  	}
   100  	return &PortAllocator{
   101  		ipMap: ipMapping{},
   102  		Begin: start,
   103  		End:   end,
   104  	}
   105  }
   106  
   107  // RequestPort requests new port from global ports pool for specified ip and proto.
   108  // If port is 0 it returns first free port. Otherwise it checks port availability
   109  // in proto's pool and returns that port or error if port is already busy.
   110  func (p *PortAllocator) RequestPort(ip net.IP, proto string, port int) (int, error) {
   111  	return p.RequestPortInRange(ip, proto, port, port)
   112  }
   113  
   114  // RequestPortInRange requests new port from global ports pool for specified ip and proto.
   115  // If portStart and portEnd are 0 it returns the first free port in the default ephemeral range.
   116  // If portStart != portEnd it returns the first free port in the requested range.
   117  // Otherwise (portStart == portEnd) it checks port availability in the requested proto's port-pool
   118  // and returns that port or error if port is already busy.
   119  func (p *PortAllocator) RequestPortInRange(ip net.IP, proto string, portStart, portEnd int) (int, error) {
   120  	p.mutex.Lock()
   121  	defer p.mutex.Unlock()
   122  
   123  	if proto != "tcp" && proto != "udp" {
   124  		return 0, ErrUnknownProtocol
   125  	}
   126  
   127  	if ip == nil {
   128  		ip = defaultIP
   129  	}
   130  	ipstr := ip.String()
   131  	protomap, ok := p.ipMap[ipstr]
   132  	if !ok {
   133  		protomap = protoMap{
   134  			"tcp": p.newPortMap(),
   135  			"udp": p.newPortMap(),
   136  		}
   137  
   138  		p.ipMap[ipstr] = protomap
   139  	}
   140  	mapping := protomap[proto]
   141  	if portStart > 0 && portStart == portEnd {
   142  		if _, ok := mapping.p[portStart]; !ok {
   143  			mapping.p[portStart] = struct{}{}
   144  			return portStart, nil
   145  		}
   146  		return 0, newErrPortAlreadyAllocated(ipstr, portStart)
   147  	}
   148  
   149  	port, err := mapping.findPort(portStart, portEnd)
   150  	if err != nil {
   151  		return 0, err
   152  	}
   153  	return port, nil
   154  }
   155  
   156  // ReleasePort releases port from global ports pool for specified ip and proto.
   157  func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error {
   158  	p.mutex.Lock()
   159  	defer p.mutex.Unlock()
   160  
   161  	if ip == nil {
   162  		ip = defaultIP
   163  	}
   164  	protomap, ok := p.ipMap[ip.String()]
   165  	if !ok {
   166  		return nil
   167  	}
   168  	delete(protomap[proto].p, port)
   169  	return nil
   170  }
   171  
   172  func (p *PortAllocator) newPortMap() *portMap {
   173  	defaultKey := getRangeKey(p.Begin, p.End)
   174  	pm := &portMap{
   175  		p:            map[int]struct{}{},
   176  		defaultRange: defaultKey,
   177  		portRanges: map[string]*portRange{
   178  			defaultKey: newPortRange(p.Begin, p.End),
   179  		},
   180  	}
   181  	return pm
   182  }
   183  
   184  // ReleaseAll releases all ports for all ips.
   185  func (p *PortAllocator) ReleaseAll() error {
   186  	p.mutex.Lock()
   187  	p.ipMap = ipMapping{}
   188  	p.mutex.Unlock()
   189  	return nil
   190  }
   191  
   192  func getRangeKey(portStart, portEnd int) string {
   193  	return fmt.Sprintf("%d-%d", portStart, portEnd)
   194  }
   195  
   196  func newPortRange(portStart, portEnd int) *portRange {
   197  	return &portRange{
   198  		begin: portStart,
   199  		end:   portEnd,
   200  		last:  portEnd,
   201  	}
   202  }
   203  
   204  func (pm *portMap) getPortRange(portStart, portEnd int) (*portRange, error) {
   205  	var key string
   206  	if portStart == 0 && portEnd == 0 {
   207  		key = pm.defaultRange
   208  	} else {
   209  		key = getRangeKey(portStart, portEnd)
   210  		if portStart == portEnd ||
   211  			portStart == 0 || portEnd == 0 ||
   212  			portEnd < portStart {
   213  			return nil, fmt.Errorf("invalid port range: %s", key)
   214  		}
   215  	}
   216  
   217  	// Return existing port range, if already known.
   218  	if pr, exists := pm.portRanges[key]; exists {
   219  		return pr, nil
   220  	}
   221  
   222  	// Otherwise create a new port range.
   223  	pr := newPortRange(portStart, portEnd)
   224  	pm.portRanges[key] = pr
   225  	return pr, nil
   226  }
   227  
   228  func (pm *portMap) findPort(portStart, portEnd int) (int, error) {
   229  	pr, err := pm.getPortRange(portStart, portEnd)
   230  	if err != nil {
   231  		return 0, err
   232  	}
   233  	port := pr.last
   234  
   235  	for i := 0; i <= pr.end-pr.begin; i++ {
   236  		port++
   237  		if port > pr.end {
   238  			port = pr.begin
   239  		}
   240  
   241  		if _, ok := pm.p[port]; !ok {
   242  			pm.p[port] = struct{}{}
   243  			pr.last = port
   244  			return port, nil
   245  		}
   246  	}
   247  	return 0, ErrAllPortsAllocated
   248  }