mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #741 from aboch/b6
Allow IPv6 allocation post endpoint create via network option
This commit is contained in:
commit
0029d6c2ef
5 changed files with 158 additions and 17 deletions
|
@ -450,6 +450,8 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ipamV6Data) > 0 {
|
if len(ipamV6Data) > 0 {
|
||||||
|
c.AddressIPv6 = ipamV6Data[0].Pool
|
||||||
|
|
||||||
if ipamV6Data[0].Gateway != nil {
|
if ipamV6Data[0].Gateway != nil {
|
||||||
c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
|
c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
|
||||||
}
|
}
|
||||||
|
@ -961,13 +963,20 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
|
||||||
if endpoint.addrv6 == nil && config.EnableIPv6 {
|
if endpoint.addrv6 == nil && config.EnableIPv6 {
|
||||||
var ip6 net.IP
|
var ip6 net.IP
|
||||||
network := n.bridge.bridgeIPv6
|
network := n.bridge.bridgeIPv6
|
||||||
|
if config.AddressIPv6 != nil {
|
||||||
|
network = config.AddressIPv6
|
||||||
|
}
|
||||||
|
|
||||||
ones, _ := network.Mask.Size()
|
ones, _ := network.Mask.Size()
|
||||||
if ones <= 80 {
|
if ones > 80 {
|
||||||
ip6 = make(net.IP, len(network.IP))
|
err = types.ForbiddenErrorf("Cannot self generate an IPv6 address on network %v: At least 48 host bits are needed.", network)
|
||||||
copy(ip6, network.IP)
|
return err
|
||||||
for i, h := range endpoint.macAddress {
|
}
|
||||||
ip6[i+10] = h
|
|
||||||
}
|
ip6 = make(net.IP, len(network.IP))
|
||||||
|
copy(ip6, network.IP)
|
||||||
|
for i, h := range endpoint.macAddress {
|
||||||
|
ip6[i+10] = h
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
|
endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
|
||||||
|
|
|
@ -111,18 +111,35 @@ func TestCreateFullOptionsLabels(t *testing.T) {
|
||||||
t.Fatalf("Failed to setup driver config: %v", err)
|
t.Fatalf("Failed to setup driver config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bndIPs := "127.0.0.1"
|
||||||
|
nwV6s := "2100:2400:2600:2700:2800::/80"
|
||||||
|
gwV6s := "2100:2400:2600:2700:2800::25/80"
|
||||||
|
nwV6, _ := types.ParseCIDR(nwV6s)
|
||||||
|
gwV6, _ := types.ParseCIDR(gwV6s)
|
||||||
|
|
||||||
labels := map[string]string{
|
labels := map[string]string{
|
||||||
BridgeName: "cu",
|
BridgeName: DefaultBridgeName,
|
||||||
|
DefaultBridge: "true",
|
||||||
netlabel.EnableIPv6: "true",
|
netlabel.EnableIPv6: "true",
|
||||||
EnableICC: "true",
|
EnableICC: "true",
|
||||||
EnableIPMasquerade: "true",
|
EnableIPMasquerade: "true",
|
||||||
DefaultBindingIP: "127.0.0.1",
|
DefaultBindingIP: bndIPs,
|
||||||
}
|
}
|
||||||
|
|
||||||
netOption := make(map[string]interface{})
|
netOption := make(map[string]interface{})
|
||||||
netOption[netlabel.GenericData] = labels
|
netOption[netlabel.GenericData] = labels
|
||||||
|
|
||||||
err := d.CreateNetwork("dummy", netOption, getIPv4Data(t), nil)
|
ipdList := getIPv4Data(t)
|
||||||
|
ipd6List := []driverapi.IPAMData{
|
||||||
|
driverapi.IPAMData{
|
||||||
|
Pool: nwV6,
|
||||||
|
AuxAddresses: map[string]*net.IPNet{
|
||||||
|
DefaultGatewayV6AuxKey: gwV6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := d.CreateNetwork("dummy", netOption, ipdList, ipd6List)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create bridge: %v", err)
|
t.Fatalf("Failed to create bridge: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -132,7 +149,7 @@ func TestCreateFullOptionsLabels(t *testing.T) {
|
||||||
t.Fatalf("Cannot find dummy network in bridge driver")
|
t.Fatalf("Cannot find dummy network in bridge driver")
|
||||||
}
|
}
|
||||||
|
|
||||||
if nw.config.BridgeName != "cu" {
|
if nw.config.BridgeName != DefaultBridgeName {
|
||||||
t.Fatalf("incongruent name in bridge network")
|
t.Fatalf("incongruent name in bridge network")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +164,36 @@ func TestCreateFullOptionsLabels(t *testing.T) {
|
||||||
if !nw.config.EnableIPMasquerade {
|
if !nw.config.EnableIPMasquerade {
|
||||||
t.Fatalf("incongruent EnableIPMasquerade in bridge network")
|
t.Fatalf("incongruent EnableIPMasquerade in bridge network")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bndIP := net.ParseIP(bndIPs)
|
||||||
|
if !bndIP.Equal(nw.config.DefaultBindingIP) {
|
||||||
|
t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !types.CompareIPNet(nw.config.AddressIPv6, nwV6) {
|
||||||
|
t.Fatalf("Unexpected: %v", nw.config.AddressIPv6)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !gwV6.IP.Equal(nw.config.DefaultGatewayIPv6) {
|
||||||
|
t.Fatalf("Unexpected: %v", nw.config.DefaultGatewayIPv6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// In short here we are testing --fixed-cidr-v6 daemon option
|
||||||
|
// plus --mac-address run option
|
||||||
|
mac, _ := net.ParseMAC("aa:bb:cc:dd:ee:ff")
|
||||||
|
epOptions := map[string]interface{}{netlabel.MacAddress: mac}
|
||||||
|
te := newTestEndpoint(ipdList[0].Pool, 20)
|
||||||
|
err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !nwV6.Contains(te.Interface().AddressIPv6().IP) {
|
||||||
|
t.Fatalf("endpoint got assigned address outside of container network(%s): %s", nwV6.String(), te.Interface().AddressIPv6())
|
||||||
|
}
|
||||||
|
if te.Interface().AddressIPv6().IP.String() != "2100:2400:2600:2700:2800:aabb:ccdd:eeff" {
|
||||||
|
t.Fatalf("Unexpected endpoint IPv6 address: %v", te.Interface().AddressIPv6().IP)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreate(t *testing.T) {
|
func TestCreate(t *testing.T) {
|
||||||
|
|
|
@ -737,7 +737,7 @@ func (ep *endpoint) DataScope() string {
|
||||||
return ep.getNetwork().DataScope()
|
return ep.getNetwork().DataScope()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ep *endpoint) assignAddress() error {
|
func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error {
|
||||||
var (
|
var (
|
||||||
ipam ipamapi.Ipam
|
ipam ipamapi.Ipam
|
||||||
err error
|
err error
|
||||||
|
@ -754,11 +754,18 @@ func (ep *endpoint) assignAddress() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = ep.assignAddressVersion(4, ipam)
|
|
||||||
if err != nil {
|
if assignIPv4 {
|
||||||
return err
|
if err = ep.assignAddressVersion(4, ipam); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ep.assignAddressVersion(6, ipam)
|
|
||||||
|
if assignIPv6 {
|
||||||
|
err = ep.assignAddressVersion(6, ipam)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
|
func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
|
||||||
|
@ -787,7 +794,11 @@ func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range ipInfo {
|
for _, d := range ipInfo {
|
||||||
addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil)
|
var prefIP net.IP
|
||||||
|
if *address != nil {
|
||||||
|
prefIP = (*address).IP
|
||||||
|
}
|
||||||
|
addr, _, err := ipam.RequestAddress(d.PoolID, prefIP, nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ep.Lock()
|
ep.Lock()
|
||||||
*address = addr
|
*address = addr
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
@ -313,6 +314,59 @@ func TestBridge(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Testing IPV6 from MAC address
|
||||||
|
func TestBridgeIpv6FromMac(t *testing.T) {
|
||||||
|
if !testutils.IsRunningInContainer() {
|
||||||
|
defer testutils.SetupTestOSContext(t)()
|
||||||
|
}
|
||||||
|
|
||||||
|
netOption := options.Generic{
|
||||||
|
netlabel.GenericData: options.Generic{
|
||||||
|
"BridgeName": "testipv6mac",
|
||||||
|
"EnableIPv6": true,
|
||||||
|
"EnableICC": true,
|
||||||
|
"EnableIPMasquerade": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ipamV4ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "192.168.100.0/24", Gateway: "192.168.100.1"}}
|
||||||
|
ipamV6ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "fe90::/64", Gateway: "fe90::22"}}
|
||||||
|
|
||||||
|
network, err := controller.NewNetwork(bridgeNetType, "testipv6mac",
|
||||||
|
libnetwork.NetworkOptionGeneric(netOption),
|
||||||
|
libnetwork.NetworkOptionIpam(ipamapi.DefaultIPAM, "", ipamV4ConfList, ipamV6ConfList),
|
||||||
|
libnetwork.NetworkOptionDeferIPv6Alloc(true))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mac := net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}
|
||||||
|
epOption := options.Generic{netlabel.MacAddress: mac}
|
||||||
|
|
||||||
|
ep, err := network.CreateEndpoint("testep", libnetwork.EndpointOptionGeneric(epOption))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
iface := ep.Info().Iface()
|
||||||
|
if !bytes.Equal(iface.MacAddress(), mac) {
|
||||||
|
t.Fatalf("Unexpected mac address: %v", iface.MacAddress())
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, expIP, _ := net.ParseCIDR("fe90::aabb:ccdd:eeff/64")
|
||||||
|
expIP.IP = ip
|
||||||
|
if !types.CompareIPNet(expIP, iface.AddressIPv6()) {
|
||||||
|
t.Fatalf("Expected %v. Got: %v", expIP, iface.AddressIPv6())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ep.Delete(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := network.Delete(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnknownDriver(t *testing.T) {
|
func TestUnknownDriver(t *testing.T) {
|
||||||
if !testutils.IsRunningInContainer() {
|
if !testutils.IsRunningInContainer() {
|
||||||
defer testutils.SetupTestOSContext(t)()
|
defer testutils.SetupTestOSContext(t)()
|
||||||
|
|
|
@ -152,6 +152,7 @@ type network struct {
|
||||||
ipamV4Info []*IpamInfo
|
ipamV4Info []*IpamInfo
|
||||||
ipamV6Info []*IpamInfo
|
ipamV6Info []*IpamInfo
|
||||||
enableIPv6 bool
|
enableIPv6 bool
|
||||||
|
postIPv6 bool
|
||||||
epCnt *endpointCnt
|
epCnt *endpointCnt
|
||||||
generic options.Generic
|
generic options.Generic
|
||||||
dbIndex uint64
|
dbIndex uint64
|
||||||
|
@ -298,6 +299,7 @@ func (n *network) CopyTo(o datastore.KVObject) error {
|
||||||
dstN.ipamType = n.ipamType
|
dstN.ipamType = n.ipamType
|
||||||
dstN.enableIPv6 = n.enableIPv6
|
dstN.enableIPv6 = n.enableIPv6
|
||||||
dstN.persist = n.persist
|
dstN.persist = n.persist
|
||||||
|
dstN.postIPv6 = n.postIPv6
|
||||||
dstN.dbIndex = n.dbIndex
|
dstN.dbIndex = n.dbIndex
|
||||||
dstN.dbExists = n.dbExists
|
dstN.dbExists = n.dbExists
|
||||||
dstN.drvOnce = n.drvOnce
|
dstN.drvOnce = n.drvOnce
|
||||||
|
@ -358,6 +360,7 @@ func (n *network) MarshalJSON() ([]byte, error) {
|
||||||
netMap["generic"] = n.generic
|
netMap["generic"] = n.generic
|
||||||
}
|
}
|
||||||
netMap["persist"] = n.persist
|
netMap["persist"] = n.persist
|
||||||
|
netMap["postIPv6"] = n.postIPv6
|
||||||
if len(n.ipamV4Config) > 0 {
|
if len(n.ipamV4Config) > 0 {
|
||||||
ics, err := json.Marshal(n.ipamV4Config)
|
ics, err := json.Marshal(n.ipamV4Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -418,6 +421,9 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
|
||||||
if v, ok := netMap["persist"]; ok {
|
if v, ok := netMap["persist"]; ok {
|
||||||
n.persist = v.(bool)
|
n.persist = v.(bool)
|
||||||
}
|
}
|
||||||
|
if v, ok := netMap["postIPv6"]; ok {
|
||||||
|
n.postIPv6 = v.(bool)
|
||||||
|
}
|
||||||
if v, ok := netMap["ipamType"]; ok {
|
if v, ok := netMap["ipamType"]; ok {
|
||||||
n.ipamType = v.(string)
|
n.ipamType = v.(string)
|
||||||
} else {
|
} else {
|
||||||
|
@ -505,6 +511,16 @@ func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
|
||||||
|
// It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
|
||||||
|
// to a container as combination of fixed-cidr-v6 + mac-address
|
||||||
|
// TODO: Remove this option setter once we support endpoint ipam options
|
||||||
|
func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
|
||||||
|
return func(n *network) {
|
||||||
|
n.postIPv6 = enable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (n *network) processOptions(options ...NetworkOption) {
|
func (n *network) processOptions(options ...NetworkOption) {
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
if opt != nil {
|
if opt != nil {
|
||||||
|
@ -655,7 +671,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
||||||
|
|
||||||
ep.processOptions(options...)
|
ep.processOptions(options...)
|
||||||
|
|
||||||
if err = ep.assignAddress(); err != nil {
|
if err = ep.assignAddress(true, !n.postIPv6); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -675,6 +691,10 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if err = ep.assignAddress(false, n.postIPv6); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err = n.getController().updateToStore(ep); err != nil {
|
if err = n.getController().updateToStore(ep); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue