github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/libnetwork/portmapper/mapper.go (about) 1 package portmapper 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "sync" 8 9 "github.com/Sirupsen/logrus" 10 "github.com/docker/libnetwork/iptables" 11 "github.com/docker/libnetwork/portallocator" 12 ) 13 14 type mapping struct { 15 proto string 16 userlandProxy userlandProxy 17 host net.Addr 18 container net.Addr 19 } 20 21 var newProxy = newProxyCommand 22 23 var ( 24 // ErrUnknownBackendAddressType refers to an unknown container or unsupported address type 25 ErrUnknownBackendAddressType = errors.New("unknown container address type not supported") 26 // ErrPortMappedForIP refers to a port already mapped to an ip address 27 ErrPortMappedForIP = errors.New("port is already mapped to ip") 28 // ErrPortNotMapped refers to an unmapped port 29 ErrPortNotMapped = errors.New("port is not mapped") 30 ) 31 32 // PortMapper manages the network address translation 33 type PortMapper struct { 34 chain *iptables.ChainInfo 35 bridgeName string 36 37 // udp:ip:port 38 currentMappings map[string]*mapping 39 lock sync.Mutex 40 41 proxyPath string 42 43 Allocator *portallocator.PortAllocator 44 } 45 46 // New returns a new instance of PortMapper 47 func New(proxyPath string) *PortMapper { 48 return NewWithPortAllocator(portallocator.Get(), proxyPath) 49 } 50 51 // NewWithPortAllocator returns a new instance of PortMapper which will use the specified PortAllocator 52 func NewWithPortAllocator(allocator *portallocator.PortAllocator, proxyPath string) *PortMapper { 53 return &PortMapper{ 54 currentMappings: make(map[string]*mapping), 55 Allocator: allocator, 56 proxyPath: proxyPath, 57 } 58 } 59 60 // SetIptablesChain sets the specified chain into portmapper 61 func (pm *PortMapper) SetIptablesChain(c *iptables.ChainInfo, bridgeName string) { 62 pm.chain = c 63 pm.bridgeName = bridgeName 64 } 65 66 // Map maps the specified container transport address to the host's network address and transport port 67 func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) { 68 return pm.MapRange(container, hostIP, hostPort, hostPort, useProxy) 69 } 70 71 // MapRange maps the specified container transport address to the host's network address and transport port range 72 func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, hostPortEnd int, useProxy bool) (host net.Addr, err error) { 73 pm.lock.Lock() 74 defer pm.lock.Unlock() 75 76 var ( 77 m *mapping 78 proto string 79 allocatedHostPort int 80 ) 81 82 switch container.(type) { 83 case *net.TCPAddr: 84 proto = "tcp" 85 if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { 86 return nil, err 87 } 88 89 m = &mapping{ 90 proto: proto, 91 host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort}, 92 container: container, 93 } 94 95 if useProxy { 96 m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port, pm.proxyPath) 97 if err != nil { 98 return nil, err 99 } 100 } else { 101 m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort) 102 } 103 case *net.UDPAddr: 104 proto = "udp" 105 if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { 106 return nil, err 107 } 108 109 m = &mapping{ 110 proto: proto, 111 host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort}, 112 container: container, 113 } 114 115 if useProxy { 116 m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port, pm.proxyPath) 117 if err != nil { 118 return nil, err 119 } 120 } else { 121 m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort) 122 } 123 default: 124 return nil, ErrUnknownBackendAddressType 125 } 126 127 // release the allocated port on any further error during return. 128 defer func() { 129 if err != nil { 130 pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort) 131 } 132 }() 133 134 key := getKey(m.host) 135 if _, exists := pm.currentMappings[key]; exists { 136 return nil, ErrPortMappedForIP 137 } 138 139 containerIP, containerPort := getIPAndPort(m.container) 140 if hostIP.To4() != nil { 141 if err := pm.forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { 142 return nil, err 143 } 144 } 145 146 cleanup := func() error { 147 // need to undo the iptables rules before we return 148 m.userlandProxy.Stop() 149 if hostIP.To4() != nil { 150 pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) 151 if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { 152 return err 153 } 154 } 155 156 return nil 157 } 158 159 if err := m.userlandProxy.Start(); err != nil { 160 if err := cleanup(); err != nil { 161 return nil, fmt.Errorf("Error during port allocation cleanup: %v", err) 162 } 163 return nil, err 164 } 165 166 pm.currentMappings[key] = m 167 return m.host, nil 168 } 169 170 // Unmap removes stored mapping for the specified host transport address 171 func (pm *PortMapper) Unmap(host net.Addr) error { 172 pm.lock.Lock() 173 defer pm.lock.Unlock() 174 175 key := getKey(host) 176 data, exists := pm.currentMappings[key] 177 if !exists { 178 return ErrPortNotMapped 179 } 180 181 if data.userlandProxy != nil { 182 data.userlandProxy.Stop() 183 } 184 185 delete(pm.currentMappings, key) 186 187 containerIP, containerPort := getIPAndPort(data.container) 188 hostIP, hostPort := getIPAndPort(data.host) 189 if err := pm.forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { 190 logrus.Errorf("Error on iptables delete: %s", err) 191 } 192 193 switch a := host.(type) { 194 case *net.TCPAddr: 195 return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port) 196 case *net.UDPAddr: 197 return pm.Allocator.ReleasePort(a.IP, "udp", a.Port) 198 } 199 return nil 200 } 201 202 //ReMapAll will re-apply all port mappings 203 func (pm *PortMapper) ReMapAll() { 204 pm.lock.Lock() 205 defer pm.lock.Unlock() 206 logrus.Debugln("Re-applying all port mappings.") 207 for _, data := range pm.currentMappings { 208 containerIP, containerPort := getIPAndPort(data.container) 209 hostIP, hostPort := getIPAndPort(data.host) 210 if err := pm.forward(iptables.Append, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { 211 logrus.Errorf("Error on iptables add: %s", err) 212 } 213 } 214 } 215 216 func getKey(a net.Addr) string { 217 switch t := a.(type) { 218 case *net.TCPAddr: 219 return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp") 220 case *net.UDPAddr: 221 return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp") 222 } 223 return "" 224 } 225 226 func getIPAndPort(a net.Addr) (net.IP, int) { 227 switch t := a.(type) { 228 case *net.TCPAddr: 229 return t.IP, t.Port 230 case *net.UDPAddr: 231 return t.IP, t.Port 232 } 233 return nil, 0 234 } 235 236 func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { 237 if pm.chain == nil { 238 return nil 239 } 240 return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName) 241 }