github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/libnetwork/endpoint_info.go (about) 1 package libnetwork 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net" 7 8 "github.com/docker/docker/libnetwork/driverapi" 9 "github.com/docker/docker/libnetwork/types" 10 ) 11 12 // EndpointInfo provides an interface to retrieve network resources bound to the endpoint. 13 type EndpointInfo interface { 14 // Iface returns information about the interface which was assigned to 15 // the endpoint by the driver. This can be used after the 16 // endpoint has been created. 17 Iface() *EndpointInterface 18 19 // Gateway returns the IPv4 gateway assigned by the driver. 20 // This will only return a valid value if a container has joined the endpoint. 21 Gateway() net.IP 22 23 // GatewayIPv6 returns the IPv6 gateway assigned by the driver. 24 // This will only return a valid value if a container has joined the endpoint. 25 GatewayIPv6() net.IP 26 27 // StaticRoutes returns the list of static routes configured by the network 28 // driver when the container joins a network 29 StaticRoutes() []*types.StaticRoute 30 31 // Sandbox returns the attached sandbox if there, nil otherwise. 32 Sandbox() *Sandbox 33 34 // LoadBalancer returns whether the endpoint is the load balancer endpoint for the network. 35 LoadBalancer() bool 36 } 37 38 // EndpointInterface holds interface addresses bound to the endpoint. 39 type EndpointInterface struct { 40 mac net.HardwareAddr 41 addr *net.IPNet 42 addrv6 *net.IPNet 43 llAddrs []*net.IPNet 44 srcName string 45 dstPrefix string 46 routes []*net.IPNet 47 v4PoolID string 48 v6PoolID string 49 } 50 51 func (epi *EndpointInterface) MarshalJSON() ([]byte, error) { 52 epMap := make(map[string]interface{}) 53 if epi.mac != nil { 54 epMap["mac"] = epi.mac.String() 55 } 56 if epi.addr != nil { 57 epMap["addr"] = epi.addr.String() 58 } 59 if epi.addrv6 != nil { 60 epMap["addrv6"] = epi.addrv6.String() 61 } 62 if len(epi.llAddrs) != 0 { 63 list := make([]string, 0, len(epi.llAddrs)) 64 for _, ll := range epi.llAddrs { 65 list = append(list, ll.String()) 66 } 67 epMap["llAddrs"] = list 68 } 69 epMap["srcName"] = epi.srcName 70 epMap["dstPrefix"] = epi.dstPrefix 71 var routes []string 72 for _, route := range epi.routes { 73 routes = append(routes, route.String()) 74 } 75 epMap["routes"] = routes 76 epMap["v4PoolID"] = epi.v4PoolID 77 epMap["v6PoolID"] = epi.v6PoolID 78 return json.Marshal(epMap) 79 } 80 81 func (epi *EndpointInterface) UnmarshalJSON(b []byte) error { 82 var ( 83 err error 84 epMap map[string]interface{} 85 ) 86 if err = json.Unmarshal(b, &epMap); err != nil { 87 return err 88 } 89 if v, ok := epMap["mac"]; ok { 90 if epi.mac, err = net.ParseMAC(v.(string)); err != nil { 91 return types.InternalErrorf("failed to decode endpoint interface mac address after json unmarshal: %s", v.(string)) 92 } 93 } 94 if v, ok := epMap["addr"]; ok { 95 if epi.addr, err = types.ParseCIDR(v.(string)); err != nil { 96 return types.InternalErrorf("failed to decode endpoint interface ipv4 address after json unmarshal: %v", err) 97 } 98 } 99 if v, ok := epMap["addrv6"]; ok { 100 if epi.addrv6, err = types.ParseCIDR(v.(string)); err != nil { 101 return types.InternalErrorf("failed to decode endpoint interface ipv6 address after json unmarshal: %v", err) 102 } 103 } 104 if v, ok := epMap["llAddrs"]; ok { 105 list := v.([]interface{}) 106 epi.llAddrs = make([]*net.IPNet, 0, len(list)) 107 for _, llS := range list { 108 ll, err := types.ParseCIDR(llS.(string)) 109 if err != nil { 110 return types.InternalErrorf("failed to decode endpoint interface link-local address (%v) after json unmarshal: %v", llS, err) 111 } 112 epi.llAddrs = append(epi.llAddrs, ll) 113 } 114 } 115 epi.srcName = epMap["srcName"].(string) 116 epi.dstPrefix = epMap["dstPrefix"].(string) 117 118 rb, _ := json.Marshal(epMap["routes"]) 119 var routes []string 120 121 // TODO(cpuguy83): linter noticed we don't check the error here... no idea why but it seems like it could introduce problems if we start checking 122 json.Unmarshal(rb, &routes) //nolint:errcheck 123 124 epi.routes = make([]*net.IPNet, 0) 125 for _, route := range routes { 126 ip, ipr, err := net.ParseCIDR(route) 127 if err == nil { 128 ipr.IP = ip 129 epi.routes = append(epi.routes, ipr) 130 } 131 } 132 epi.v4PoolID = epMap["v4PoolID"].(string) 133 epi.v6PoolID = epMap["v6PoolID"].(string) 134 135 return nil 136 } 137 138 func (epi *EndpointInterface) CopyTo(dstEpi *EndpointInterface) error { 139 dstEpi.mac = types.GetMacCopy(epi.mac) 140 dstEpi.addr = types.GetIPNetCopy(epi.addr) 141 dstEpi.addrv6 = types.GetIPNetCopy(epi.addrv6) 142 dstEpi.srcName = epi.srcName 143 dstEpi.dstPrefix = epi.dstPrefix 144 dstEpi.v4PoolID = epi.v4PoolID 145 dstEpi.v6PoolID = epi.v6PoolID 146 if len(epi.llAddrs) != 0 { 147 dstEpi.llAddrs = make([]*net.IPNet, 0, len(epi.llAddrs)) 148 dstEpi.llAddrs = append(dstEpi.llAddrs, epi.llAddrs...) 149 } 150 151 for _, route := range epi.routes { 152 dstEpi.routes = append(dstEpi.routes, types.GetIPNetCopy(route)) 153 } 154 155 return nil 156 } 157 158 type endpointJoinInfo struct { 159 gw net.IP 160 gw6 net.IP 161 StaticRoutes []*types.StaticRoute 162 driverTableEntries []*tableEntry 163 disableGatewayService bool 164 } 165 166 type tableEntry struct { 167 tableName string 168 key string 169 value []byte 170 } 171 172 // Info hydrates the endpoint and returns certain operational data belonging 173 // to this endpoint. 174 // 175 // TODO(thaJeztah): make sure that Endpoint is always fully hydrated, and remove the EndpointInfo interface, and use Endpoint directly. 176 func (ep *Endpoint) Info() EndpointInfo { 177 if ep.sandboxID != "" { 178 return ep 179 } 180 n, err := ep.getNetworkFromStore() 181 if err != nil { 182 return nil 183 } 184 185 ep, err = n.getEndpointFromStore(ep.ID()) 186 if err != nil { 187 return nil 188 } 189 190 sb, ok := ep.getSandbox() 191 if !ok { 192 // endpoint hasn't joined any sandbox. 193 // Just return the endpoint 194 return ep 195 } 196 197 return sb.GetEndpoint(ep.ID()) 198 } 199 200 // Iface returns information about the interface which was assigned to 201 // the endpoint by the driver. This can be used after the 202 // endpoint has been created. 203 func (ep *Endpoint) Iface() *EndpointInterface { 204 ep.mu.Lock() 205 defer ep.mu.Unlock() 206 return ep.iface 207 } 208 209 // SetMacAddress allows the driver to set the mac address to the endpoint interface 210 // during the call to CreateEndpoint, if the mac address is not already set. 211 func (epi *EndpointInterface) SetMacAddress(mac net.HardwareAddr) error { 212 if epi.mac != nil { 213 return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", epi.mac, mac) 214 } 215 if mac == nil { 216 return types.InvalidParameterErrorf("tried to set nil MAC address to endpoint interface") 217 } 218 epi.mac = types.GetMacCopy(mac) 219 return nil 220 } 221 222 func (epi *EndpointInterface) SetIPAddress(address *net.IPNet) error { 223 if address.IP == nil { 224 return types.InvalidParameterErrorf("tried to set nil IP address to endpoint interface") 225 } 226 if address.IP.To4() == nil { 227 return setAddress(&epi.addrv6, address) 228 } 229 return setAddress(&epi.addr, address) 230 } 231 232 func setAddress(ifaceAddr **net.IPNet, address *net.IPNet) error { 233 if *ifaceAddr != nil { 234 return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address) 235 } 236 *ifaceAddr = types.GetIPNetCopy(address) 237 return nil 238 } 239 240 // MacAddress returns the MAC address assigned to the endpoint. 241 func (epi *EndpointInterface) MacAddress() net.HardwareAddr { 242 return types.GetMacCopy(epi.mac) 243 } 244 245 // Address returns the IPv4 address assigned to the endpoint. 246 func (epi *EndpointInterface) Address() *net.IPNet { 247 return types.GetIPNetCopy(epi.addr) 248 } 249 250 // AddressIPv6 returns the IPv6 address assigned to the endpoint. 251 func (epi *EndpointInterface) AddressIPv6() *net.IPNet { 252 return types.GetIPNetCopy(epi.addrv6) 253 } 254 255 // LinkLocalAddresses returns the list of link-local (IPv4/IPv6) addresses assigned to the endpoint. 256 func (epi *EndpointInterface) LinkLocalAddresses() []*net.IPNet { 257 return epi.llAddrs 258 } 259 260 // SrcName returns the name of the interface w/in the container 261 func (epi *EndpointInterface) SrcName() string { 262 return epi.srcName 263 } 264 265 // SetNames method assigns the srcName and dstPrefix for the interface. 266 func (epi *EndpointInterface) SetNames(srcName string, dstPrefix string) error { 267 epi.srcName = srcName 268 epi.dstPrefix = dstPrefix 269 return nil 270 } 271 272 func (ep *Endpoint) InterfaceName() driverapi.InterfaceNameInfo { 273 ep.mu.Lock() 274 defer ep.mu.Unlock() 275 return ep.iface 276 } 277 278 // AddStaticRoute adds a route to the sandbox. 279 // It may be used in addition to or instead of a default gateway (as above). 280 func (ep *Endpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error { 281 ep.mu.Lock() 282 defer ep.mu.Unlock() 283 if routeType == types.NEXTHOP { 284 // If the route specifies a next-hop, then it's loosely routed (i.e. not bound to a particular interface). 285 ep.joinInfo.StaticRoutes = append(ep.joinInfo.StaticRoutes, &types.StaticRoute{ 286 Destination: destination, 287 RouteType: routeType, 288 NextHop: nextHop, 289 }) 290 } else { 291 // If the route doesn't specify a next-hop, it must be a connected route, bound to an interface. 292 ep.iface.routes = append(ep.iface.routes, destination) 293 } 294 return nil 295 } 296 297 // AddTableEntry adds a table entry to the gossip layer 298 // passing the table name, key and an opaque value. 299 func (ep *Endpoint) AddTableEntry(tableName, key string, value []byte) error { 300 ep.mu.Lock() 301 defer ep.mu.Unlock() 302 303 ep.joinInfo.driverTableEntries = append(ep.joinInfo.driverTableEntries, &tableEntry{ 304 tableName: tableName, 305 key: key, 306 value: value, 307 }) 308 309 return nil 310 } 311 312 // Sandbox returns the attached sandbox if there, nil otherwise. 313 func (ep *Endpoint) Sandbox() *Sandbox { 314 cnt, ok := ep.getSandbox() 315 if !ok { 316 return nil 317 } 318 return cnt 319 } 320 321 // LoadBalancer returns whether the endpoint is the load balancer endpoint for the network. 322 func (ep *Endpoint) LoadBalancer() bool { 323 ep.mu.Lock() 324 defer ep.mu.Unlock() 325 return ep.loadBalancer 326 } 327 328 // StaticRoutes returns the list of static routes configured by the network 329 // driver when the container joins a network 330 func (ep *Endpoint) StaticRoutes() []*types.StaticRoute { 331 ep.mu.Lock() 332 defer ep.mu.Unlock() 333 334 if ep.joinInfo == nil { 335 return nil 336 } 337 338 return ep.joinInfo.StaticRoutes 339 } 340 341 // Gateway returns the IPv4 gateway assigned by the driver. 342 // This will only return a valid value if a container has joined the endpoint. 343 func (ep *Endpoint) Gateway() net.IP { 344 ep.mu.Lock() 345 defer ep.mu.Unlock() 346 347 if ep.joinInfo == nil { 348 return net.IP{} 349 } 350 351 return types.GetIPCopy(ep.joinInfo.gw) 352 } 353 354 // GatewayIPv6 returns the IPv6 gateway assigned by the driver. 355 // This will only return a valid value if a container has joined the endpoint. 356 func (ep *Endpoint) GatewayIPv6() net.IP { 357 ep.mu.Lock() 358 defer ep.mu.Unlock() 359 360 if ep.joinInfo == nil { 361 return net.IP{} 362 } 363 364 return types.GetIPCopy(ep.joinInfo.gw6) 365 } 366 367 // SetGateway sets the default IPv4 gateway when a container joins the endpoint. 368 func (ep *Endpoint) SetGateway(gw net.IP) error { 369 ep.mu.Lock() 370 defer ep.mu.Unlock() 371 372 ep.joinInfo.gw = types.GetIPCopy(gw) 373 return nil 374 } 375 376 // SetGatewayIPv6 sets the default IPv6 gateway when a container joins the endpoint. 377 func (ep *Endpoint) SetGatewayIPv6(gw6 net.IP) error { 378 ep.mu.Lock() 379 defer ep.mu.Unlock() 380 381 ep.joinInfo.gw6 = types.GetIPCopy(gw6) 382 return nil 383 } 384 385 func (ep *Endpoint) retrieveFromStore() (*Endpoint, error) { 386 n, err := ep.getNetworkFromStore() 387 if err != nil { 388 return nil, fmt.Errorf("could not find network in store to get latest endpoint %s: %v", ep.Name(), err) 389 } 390 return n.getEndpointFromStore(ep.ID()) 391 } 392 393 // DisableGatewayService tells libnetwork not to provide Default GW for the container 394 func (ep *Endpoint) DisableGatewayService() { 395 ep.mu.Lock() 396 defer ep.mu.Unlock() 397 398 ep.joinInfo.disableGatewayService = true 399 } 400 401 func (epj *endpointJoinInfo) MarshalJSON() ([]byte, error) { 402 epMap := make(map[string]interface{}) 403 if epj.gw != nil { 404 epMap["gw"] = epj.gw.String() 405 } 406 if epj.gw6 != nil { 407 epMap["gw6"] = epj.gw6.String() 408 } 409 epMap["disableGatewayService"] = epj.disableGatewayService 410 epMap["StaticRoutes"] = epj.StaticRoutes 411 return json.Marshal(epMap) 412 } 413 414 func (epj *endpointJoinInfo) UnmarshalJSON(b []byte) error { 415 var ( 416 err error 417 epMap map[string]interface{} 418 ) 419 if err = json.Unmarshal(b, &epMap); err != nil { 420 return err 421 } 422 if v, ok := epMap["gw"]; ok { 423 epj.gw = net.ParseIP(v.(string)) 424 } 425 if v, ok := epMap["gw6"]; ok { 426 epj.gw6 = net.ParseIP(v.(string)) 427 } 428 epj.disableGatewayService = epMap["disableGatewayService"].(bool) 429 430 var tStaticRoute []types.StaticRoute 431 if v, ok := epMap["StaticRoutes"]; ok { 432 tb, _ := json.Marshal(v) 433 var tStaticRoute []types.StaticRoute 434 // TODO(cpuguy83): Linter caught that we aren't checking errors here 435 // I don't know why we aren't other than potentially the data is not always expected to be right? 436 // This is why I'm not adding the error check. 437 // 438 // In any case for posterity please if you figure this out document it or check the error 439 json.Unmarshal(tb, &tStaticRoute) //nolint:errcheck 440 } 441 var StaticRoutes []*types.StaticRoute 442 for _, r := range tStaticRoute { 443 r := r 444 StaticRoutes = append(StaticRoutes, &r) 445 } 446 epj.StaticRoutes = StaticRoutes 447 448 return nil 449 } 450 451 func (epj *endpointJoinInfo) CopyTo(dstEpj *endpointJoinInfo) error { 452 dstEpj.disableGatewayService = epj.disableGatewayService 453 dstEpj.StaticRoutes = make([]*types.StaticRoute, len(epj.StaticRoutes)) 454 copy(dstEpj.StaticRoutes, epj.StaticRoutes) 455 dstEpj.driverTableEntries = make([]*tableEntry, len(epj.driverTableEntries)) 456 copy(dstEpj.driverTableEntries, epj.driverTableEntries) 457 dstEpj.gw = types.GetIPCopy(epj.gw) 458 dstEpj.gw6 = types.GetIPCopy(epj.gw6) 459 return nil 460 }