github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/socket/netlink/port/port.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package port provides port ID allocation for netlink sockets. 16 // 17 // A netlink port is any int32 value. Positive ports are typically equivalent 18 // to the PID of the binding process. If that port is unavailable, negative 19 // ports are searched to find a free port that will not conflict with other 20 // PIDS. 21 package port 22 23 import ( 24 "fmt" 25 "math" 26 "math/rand" 27 28 "github.com/nicocha30/gvisor-ligolo/pkg/sync" 29 ) 30 31 // maxPorts is a sanity limit on the maximum number of ports to allocate per 32 // protocol. 33 const maxPorts = 10000 34 35 // Manager allocates netlink port IDs. 36 // 37 // +stateify savable 38 type Manager struct { 39 // mu protects the fields below. 40 mu sync.Mutex `state:"nosave"` 41 42 // ports contains a map of allocated ports for each protocol. 43 ports map[int]map[int32]struct{} 44 } 45 46 // New creates a new Manager. 47 func New() *Manager { 48 return &Manager{ 49 ports: make(map[int]map[int32]struct{}), 50 } 51 } 52 53 // Allocate reserves a new port ID for protocol. hint will be taken if 54 // available. 55 func (m *Manager) Allocate(protocol int, hint int32) (int32, bool) { 56 m.mu.Lock() 57 defer m.mu.Unlock() 58 59 proto, ok := m.ports[protocol] 60 if !ok { 61 proto = make(map[int32]struct{}) 62 // Port 0 is reserved for the kernel. 63 proto[0] = struct{}{} 64 m.ports[protocol] = proto 65 } 66 67 if len(proto) >= maxPorts { 68 return 0, false 69 } 70 71 if _, ok := proto[hint]; !ok { 72 // Hint is available, reserve it. 73 proto[hint] = struct{}{} 74 return hint, true 75 } 76 77 // Search for any free port in [math.MinInt32, -4096). The positive 78 // port space is left open for pid-based allocations. This behavior is 79 // consistent with Linux. 80 start := int32(math.MinInt32 + rand.Int63n(math.MaxInt32-4096+1)) 81 curr := start 82 for { 83 if _, ok := proto[curr]; !ok { 84 proto[curr] = struct{}{} 85 return curr, true 86 } 87 88 curr-- 89 if curr >= -4096 { 90 curr = -4097 91 } 92 if curr == start { 93 // Nothing found. We should always find a free port 94 // because maxPorts < -4096 - MinInt32. 95 panic(fmt.Sprintf("No free port found in %+v", proto)) 96 } 97 } 98 } 99 100 // Release frees the specified port for protocol. 101 // 102 // Preconditions: port is already allocated. 103 func (m *Manager) Release(protocol int, port int32) { 104 m.mu.Lock() 105 defer m.mu.Unlock() 106 107 proto, ok := m.ports[protocol] 108 if !ok { 109 panic(fmt.Sprintf("Released port %d for protocol %d which has no allocations", port, protocol)) 110 } 111 112 if _, ok := proto[port]; !ok { 113 panic(fmt.Sprintf("Released port %d for protocol %d is not allocated", port, protocol)) 114 } 115 116 delete(proto, port) 117 }