mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
libnetwork to handle allocation of ipv6
Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
parent
d80f34b8e8
commit
b9596c89d6
5 changed files with 169 additions and 71 deletions
|
@ -343,15 +343,13 @@ func (c *controller) NewNetwork(networkType, name string, options ...NetworkOpti
|
|||
return nil, err
|
||||
}
|
||||
|
||||
cnfs, err := network.ipamAllocate()
|
||||
err := network.ipamAllocate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
for _, cn := range cnfs {
|
||||
cn()
|
||||
}
|
||||
network.ipamRelease()
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -386,7 +384,7 @@ func (c *controller) addNetwork(n *network) error {
|
|||
}
|
||||
|
||||
// Create the network
|
||||
if err := d.CreateNetwork(n.id, n.generic, n.getIPv4Data(), n.getIPv6Data()); err != nil {
|
||||
if err := d.CreateNetwork(n.id, n.generic, n.getIPData(4), n.getIPData(6)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -630,13 +630,44 @@ func (ep *endpoint) assignAddress() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, d := range n.getIPInfo() {
|
||||
var addr *net.IPNet
|
||||
addr, _, err = ipam.RequestAddress(d.PoolID, nil, nil)
|
||||
err = ep.assignAddressVersion(4, ipam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ep.assignAddressVersion(6, ipam)
|
||||
}
|
||||
|
||||
func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
|
||||
var (
|
||||
poolID *string
|
||||
address **net.IPNet
|
||||
)
|
||||
|
||||
n := ep.getNetwork()
|
||||
switch ipVer {
|
||||
case 4:
|
||||
poolID = &ep.iface.v4PoolID
|
||||
address = &ep.iface.addr
|
||||
case 6:
|
||||
poolID = &ep.iface.v6PoolID
|
||||
address = &ep.iface.addrv6
|
||||
default:
|
||||
return types.InternalErrorf("incorrect ip version number passed: %d", ipVer)
|
||||
}
|
||||
|
||||
ipInfo := n.getIPInfo(ipVer)
|
||||
|
||||
// ipv6 address is not mandatory
|
||||
if len(ipInfo) == 0 && ipVer == 6 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, d := range ipInfo {
|
||||
addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil)
|
||||
if err == nil {
|
||||
ep.Lock()
|
||||
ep.iface.addr = addr
|
||||
ep.iface.poolID = d.PoolID
|
||||
*address = addr
|
||||
*poolID = d.PoolID
|
||||
ep.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
@ -644,7 +675,7 @@ func (ep *endpoint) assignAddress() error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("no available ip addresses on this network address pools: %s (%s)", n.Name(), n.ID())
|
||||
return fmt.Errorf("no available IPv%d addresses on this network's address pools: %s (%s)", ipVer, n.Name(), n.ID())
|
||||
}
|
||||
|
||||
func (ep *endpoint) releaseAddress() {
|
||||
|
@ -657,7 +688,12 @@ func (ep *endpoint) releaseAddress() {
|
|||
log.Warnf("Failed to retrieve ipam driver to release interface address on delete of endpoint %s (%s): %v", ep.Name(), ep.ID(), err)
|
||||
return
|
||||
}
|
||||
if err := ipam.ReleaseAddress(ep.iface.poolID, ep.iface.addr.IP); err != nil {
|
||||
if err := ipam.ReleaseAddress(ep.iface.v4PoolID, ep.iface.addr.IP); err != nil {
|
||||
log.Warnf("Failed to release ip address %s on delete of endpoint %s (%s): %v", ep.iface.addr.IP, ep.Name(), ep.ID(), err)
|
||||
}
|
||||
if ep.iface.addrv6 != nil && ep.iface.addrv6.IP.IsGlobalUnicast() {
|
||||
if err := ipam.ReleaseAddress(ep.iface.v6PoolID, ep.iface.addrv6.IP); err != nil {
|
||||
log.Warnf("Failed to release ip address %s on delete of endpoint %s (%s): %v", ep.iface.addrv6.IP, ep.Name(), ep.ID(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,8 @@ type endpointInterface struct {
|
|||
srcName string
|
||||
dstPrefix string
|
||||
routes []*net.IPNet
|
||||
poolID string
|
||||
v4PoolID string
|
||||
v6PoolID string
|
||||
}
|
||||
|
||||
func (epi *endpointInterface) MarshalJSON() ([]byte, error) {
|
||||
|
@ -69,7 +70,8 @@ func (epi *endpointInterface) MarshalJSON() ([]byte, error) {
|
|||
routes = append(routes, route.String())
|
||||
}
|
||||
epMap["routes"] = routes
|
||||
epMap["poolID"] = epi.poolID
|
||||
epMap["v4PoolID"] = epi.v4PoolID
|
||||
epMap["v6PoolID"] = epi.v6PoolID
|
||||
return json.Marshal(epMap)
|
||||
}
|
||||
|
||||
|
@ -111,7 +113,8 @@ func (epi *endpointInterface) UnmarshalJSON(b []byte) error {
|
|||
epi.routes = append(epi.routes, ipr)
|
||||
}
|
||||
}
|
||||
epi.poolID = epMap["poolID"].(string)
|
||||
epi.v4PoolID = epMap["v4PoolID"].(string)
|
||||
epi.v6PoolID = epMap["v6PoolID"].(string)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -122,7 +125,8 @@ func (epi *endpointInterface) CopyTo(dstEpi *endpointInterface) error {
|
|||
dstEpi.addrv6 = types.GetIPNetCopy(epi.addrv6)
|
||||
dstEpi.srcName = epi.srcName
|
||||
dstEpi.dstPrefix = epi.dstPrefix
|
||||
dstEpi.poolID = epi.poolID
|
||||
dstEpi.v4PoolID = epi.v4PoolID
|
||||
dstEpi.v6PoolID = epi.v6PoolID
|
||||
|
||||
for _, route := range epi.routes {
|
||||
dstEpi.routes = append(dstEpi.routes, types.GetIPNetCopy(route))
|
||||
|
|
|
@ -197,7 +197,8 @@ func TestEndpointMarshalling(t *testing.T) {
|
|||
addrv6: nw6,
|
||||
srcName: "veth12ab1314",
|
||||
dstPrefix: "eth",
|
||||
poolID: "poolpool",
|
||||
v4PoolID: "poolpool",
|
||||
v6PoolID: "poolv6",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -224,7 +225,7 @@ func compareEndpointInterface(a, b *endpointInterface) bool {
|
|||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
return a.srcName == b.srcName && a.dstPrefix == b.dstPrefix && a.poolID == b.poolID &&
|
||||
return a.srcName == b.srcName && a.dstPrefix == b.dstPrefix && a.v4PoolID == b.v4PoolID && a.v6PoolID == b.v6PoolID &&
|
||||
types.CompareIPNet(a.addr, b.addr) && types.CompareIPNet(a.addrv6, b.addrv6)
|
||||
}
|
||||
|
||||
|
@ -319,7 +320,7 @@ func TestAuxAddresses(t *testing.T) {
|
|||
|
||||
n.ipamV4Config = []*IpamConf{&IpamConf{PreferredPool: i.masterPool, SubPool: i.subPool, AuxAddresses: i.auxAddresses}}
|
||||
|
||||
_, err := n.ipamAllocate()
|
||||
err = n.ipamAllocate()
|
||||
|
||||
if i.good != (err == nil) {
|
||||
t.Fatalf("Unexpected result for %v: %v", i, err)
|
||||
|
|
|
@ -65,7 +65,7 @@ type IpamConf struct {
|
|||
// this becomes the container pool
|
||||
SubPool string
|
||||
// Input options for IPAM Driver (optional)
|
||||
Options map[string]string // IPAM input options
|
||||
Options map[string]string
|
||||
// IPv6 flag, Needed when no preferred pool is specified
|
||||
IsV6 bool
|
||||
// Preferred Network Gateway address (optional)
|
||||
|
@ -281,6 +281,14 @@ func (n *network) CopyTo(o datastore.KVObject) error {
|
|||
dstN.ipamV4Info = append(dstN.ipamV4Info, dstV4Info)
|
||||
}
|
||||
|
||||
if n.ipamV6Info != nil {
|
||||
for _, v6info := range n.ipamV6Info {
|
||||
dstV6Info := &IpamInfo{}
|
||||
v6info.CopyTo(dstV6Info)
|
||||
dstN.ipamV6Info = append(dstN.ipamV6Info, dstV6Info)
|
||||
}
|
||||
}
|
||||
|
||||
dstN.generic = options.Generic{}
|
||||
for k, v := range n.generic {
|
||||
dstN.generic[k] = v
|
||||
|
@ -786,44 +794,76 @@ func (n *network) getController() *controller {
|
|||
return n.ctrlr
|
||||
}
|
||||
|
||||
func (n *network) ipamAllocate() ([]func(), error) {
|
||||
var (
|
||||
cnl []func()
|
||||
err error
|
||||
)
|
||||
|
||||
func (n *network) ipamAllocate() error {
|
||||
// For now also exclude bridge from using new ipam
|
||||
if n.Type() == "host" || n.Type() == "null" || n.Type() == "bridge" {
|
||||
return cnl, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
ipam, err := n.getController().getIpamDriver(n.ipamType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
if n.addrSpace == "" {
|
||||
if n.addrSpace, err = n.deriveAddressSpace(); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if n.ipamV4Config == nil {
|
||||
n.ipamV4Config = []*IpamConf{&IpamConf{}}
|
||||
err = n.ipamAllocateVersion(4, ipam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.ipamV4Info = make([]*IpamInfo, len(n.ipamV4Config))
|
||||
defer func() {
|
||||
if err != nil {
|
||||
n.ipamReleaseVersion(4, ipam)
|
||||
}
|
||||
}()
|
||||
|
||||
for i, cfg := range n.ipamV4Config {
|
||||
return n.ipamAllocateVersion(6, ipam)
|
||||
}
|
||||
|
||||
func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
|
||||
var (
|
||||
cfgList *[]*IpamConf
|
||||
infoList *[]*IpamInfo
|
||||
err error
|
||||
)
|
||||
|
||||
switch ipVer {
|
||||
case 4:
|
||||
cfgList = &n.ipamV4Config
|
||||
infoList = &n.ipamV4Info
|
||||
case 6:
|
||||
cfgList = &n.ipamV6Config
|
||||
infoList = &n.ipamV6Info
|
||||
default:
|
||||
return types.InternalErrorf("incorrect ip version passed to ipam allocate: %d", ipVer)
|
||||
}
|
||||
|
||||
if *cfgList == nil {
|
||||
if ipVer == 6 {
|
||||
return nil
|
||||
}
|
||||
*cfgList = []*IpamConf{&IpamConf{}}
|
||||
}
|
||||
|
||||
*infoList = make([]*IpamInfo, len(*cfgList))
|
||||
|
||||
log.Debugf("allocating IPv%d pools for network %s (%s)", ipVer, n.Name(), n.ID())
|
||||
|
||||
for i, cfg := range *cfgList {
|
||||
if err = cfg.Validate(); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
d := &IpamInfo{}
|
||||
n.ipamV4Info[i] = d
|
||||
(*infoList)[i] = d
|
||||
|
||||
d.PoolID, d.Pool, d.Meta, err = ipam.RequestPool(n.addrSpace, cfg.PreferredPool, cfg.SubPool, cfg.Options, cfg.IsV6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
@ -836,7 +876,7 @@ func (n *network) ipamAllocate() ([]func(), error) {
|
|||
|
||||
if gws, ok := d.Meta[netlabel.Gateway]; ok {
|
||||
if d.Gateway, err = types.ParseCIDR(gws); err != nil {
|
||||
return nil, types.BadRequestErrorf("failed to parse gateway address (%v) returned by ipam driver: %v", gws, err)
|
||||
return types.BadRequestErrorf("failed to parse gateway address (%v) returned by ipam driver: %v", gws, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -845,15 +885,10 @@ func (n *network) ipamAllocate() ([]func(), error) {
|
|||
// If none of the above is true, libnetwork will allocate one.
|
||||
if cfg.Gateway != "" || d.Gateway == nil {
|
||||
if d.Gateway, _, err = ipam.RequestAddress(d.PoolID, net.ParseIP(cfg.Gateway), nil); err != nil {
|
||||
return nil, types.InternalErrorf("failed to allocate gateway (%v): %v", cfg.Gateway, err)
|
||||
return types.InternalErrorf("failed to allocate gateway (%v): %v", cfg.Gateway, err)
|
||||
}
|
||||
}
|
||||
|
||||
cnl = append(cnl, func() {
|
||||
if err := ipam.ReleaseAddress(d.PoolID, d.Gateway.IP); err != nil {
|
||||
log.Warnf("Failed to release gw address %s after failure to create network %s (%s)", d.Gateway, n.Name(), n.ID())
|
||||
}
|
||||
})
|
||||
// Auxiliary addresses must be part of the master address pool
|
||||
// If they fall into the container addressable pool, libnetwork will reserve them
|
||||
if cfg.AuxAddresses != nil {
|
||||
|
@ -861,27 +896,20 @@ func (n *network) ipamAllocate() ([]func(), error) {
|
|||
d.IPAMData.AuxAddresses = make(map[string]*net.IPNet, len(cfg.AuxAddresses))
|
||||
for k, v := range cfg.AuxAddresses {
|
||||
if ip = net.ParseIP(v); ip == nil {
|
||||
return nil, types.BadRequestErrorf("non parsable secondary ip address (%s:%s) passed for network %s", k, v, n.Name())
|
||||
return types.BadRequestErrorf("non parsable secondary ip address (%s:%s) passed for network %s", k, v, n.Name())
|
||||
}
|
||||
if !d.Pool.Contains(ip) {
|
||||
return cnl, types.ForbiddenErrorf("auxilairy address: (%s:%s) must belong to the master pool: %s", k, v, d.Pool)
|
||||
return types.ForbiddenErrorf("auxilairy address: (%s:%s) must belong to the master pool: %s", k, v, d.Pool)
|
||||
}
|
||||
// Attempt reservation in the container addressable pool, silent the error if address does not belong to that pool
|
||||
if d.IPAMData.AuxAddresses[k], _, err = ipam.RequestAddress(d.PoolID, ip, nil); err != nil && err != ipamapi.ErrIPOutOfRange {
|
||||
return nil, types.InternalErrorf("failed to allocate secondary ip address (%s:%s): %v", k, v, err)
|
||||
}
|
||||
if err == nil {
|
||||
cnl = append(cnl, func() {
|
||||
if err := ipam.ReleaseAddress(d.PoolID, ip); err != nil {
|
||||
log.Warnf("Failed to release secondary ip address %s(%s) after failure to create network %s (%s)", k, v, ip, n.Name(), n.ID())
|
||||
}
|
||||
})
|
||||
return types.InternalErrorf("failed to allocate secondary ip address (%s:%s): %v", k, v, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cnl, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) ipamRelease() {
|
||||
|
@ -894,7 +922,30 @@ func (n *network) ipamRelease() {
|
|||
log.Warnf("Failed to retrieve ipam driver to release address pool(s) on delete of network %s (%s): %v", n.Name(), n.ID(), err)
|
||||
return
|
||||
}
|
||||
for _, d := range n.ipamV4Info {
|
||||
n.ipamReleaseVersion(4, ipam)
|
||||
n.ipamReleaseVersion(6, ipam)
|
||||
}
|
||||
|
||||
func (n *network) ipamReleaseVersion(ipVer int, ipam ipamapi.Ipam) {
|
||||
var infoList []*IpamInfo
|
||||
|
||||
switch ipVer {
|
||||
case 4:
|
||||
infoList = n.ipamV4Info
|
||||
case 6:
|
||||
infoList = n.ipamV6Info
|
||||
default:
|
||||
log.Warnf("incorrect ip version passed to ipam release: %d", ipVer)
|
||||
return
|
||||
}
|
||||
|
||||
if infoList == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("releasing IPv%d pools from network %s (%s)", ipVer, n.Name(), n.ID())
|
||||
|
||||
for _, d := range infoList {
|
||||
if d.Gateway != nil {
|
||||
if err := ipam.ReleaseAddress(d.PoolID, d.Gateway.IP); err != nil {
|
||||
log.Warnf("Failed to release gateway ip address %s on delete of network %s (%s): %v", d.Gateway.IP, n.Name(), n.ID(), err)
|
||||
|
@ -915,30 +966,38 @@ func (n *network) ipamRelease() {
|
|||
}
|
||||
}
|
||||
|
||||
func (n *network) getIPInfo() []*IpamInfo {
|
||||
func (n *network) getIPInfo(ipVer int) []*IpamInfo {
|
||||
var info []*IpamInfo
|
||||
switch ipVer {
|
||||
case 4:
|
||||
info = n.ipamV4Info
|
||||
case 6:
|
||||
info = n.ipamV6Info
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
l := make([]*IpamInfo, 0, len(info))
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
l := make([]*IpamInfo, 0, len(n.ipamV4Info))
|
||||
for _, d := range n.ipamV4Info {
|
||||
for _, d := range info {
|
||||
l = append(l, d)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (n *network) getIPv4Data() []driverapi.IPAMData {
|
||||
l := make([]driverapi.IPAMData, 0, len(n.ipamV4Info))
|
||||
n.Lock()
|
||||
for _, d := range n.ipamV4Info {
|
||||
l = append(l, d.IPAMData)
|
||||
}
|
||||
n.Unlock()
|
||||
return l
|
||||
}
|
||||
|
||||
func (n *network) getIPv6Data() []driverapi.IPAMData {
|
||||
l := make([]driverapi.IPAMData, 0, len(n.ipamV6Info))
|
||||
func (n *network) getIPData(ipVer int) []driverapi.IPAMData {
|
||||
var info []*IpamInfo
|
||||
switch ipVer {
|
||||
case 4:
|
||||
info = n.ipamV4Info
|
||||
case 6:
|
||||
info = n.ipamV6Info
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
l := make([]driverapi.IPAMData, 0, len(info))
|
||||
n.Lock()
|
||||
for _, d := range n.ipamV6Info {
|
||||
for _, d := range info {
|
||||
l = append(l, d.IPAMData)
|
||||
}
|
||||
n.Unlock()
|
||||
|
|
Loading…
Reference in a new issue