Browse code

Allow IPv6 allocation post endpoint create

- Controlled by network option

Signed-off-by: Alessandro Boch <aboch@docker.com>

Alessandro Boch authored on 2015/11/06 16:50:10
Showing 5 changed files
... ...
@@ -450,6 +450,8 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
450 450
 	}
451 451
 
452 452
 	if len(ipamV6Data) > 0 {
453
+		c.AddressIPv6 = ipamV6Data[0].Pool
454
+
453 455
 		if ipamV6Data[0].Gateway != nil {
454 456
 			c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
455 457
 		}
... ...
@@ -959,13 +961,20 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
959 959
 	if endpoint.addrv6 == nil && config.EnableIPv6 {
960 960
 		var ip6 net.IP
961 961
 		network := n.bridge.bridgeIPv6
962
+		if config.AddressIPv6 != nil {
963
+			network = config.AddressIPv6
964
+		}
965
+
962 966
 		ones, _ := network.Mask.Size()
963
-		if ones <= 80 {
964
-			ip6 = make(net.IP, len(network.IP))
965
-			copy(ip6, network.IP)
966
-			for i, h := range endpoint.macAddress {
967
-				ip6[i+10] = h
968
-			}
967
+		if ones > 80 {
968
+			err = types.ForbiddenErrorf("Cannot self generate an IPv6 address on network %v: At least 48 host bits are needed.", network)
969
+			return err
970
+		}
971
+
972
+		ip6 = make(net.IP, len(network.IP))
973
+		copy(ip6, network.IP)
974
+		for i, h := range endpoint.macAddress {
975
+			ip6[i+10] = h
969 976
 		}
970 977
 
971 978
 		endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
... ...
@@ -111,18 +111,35 @@ func TestCreateFullOptionsLabels(t *testing.T) {
111 111
 		t.Fatalf("Failed to setup driver config: %v", err)
112 112
 	}
113 113
 
114
+	bndIPs := "127.0.0.1"
115
+	nwV6s := "2100:2400:2600:2700:2800::/80"
116
+	gwV6s := "2100:2400:2600:2700:2800::25/80"
117
+	nwV6, _ := types.ParseCIDR(nwV6s)
118
+	gwV6, _ := types.ParseCIDR(gwV6s)
119
+
114 120
 	labels := map[string]string{
115
-		BridgeName:          "cu",
121
+		BridgeName:          DefaultBridgeName,
122
+		DefaultBridge:       "true",
116 123
 		netlabel.EnableIPv6: "true",
117 124
 		EnableICC:           "true",
118 125
 		EnableIPMasquerade:  "true",
119
-		DefaultBindingIP:    "127.0.0.1",
126
+		DefaultBindingIP:    bndIPs,
120 127
 	}
121 128
 
122 129
 	netOption := make(map[string]interface{})
123 130
 	netOption[netlabel.GenericData] = labels
124 131
 
125
-	err := d.CreateNetwork("dummy", netOption, getIPv4Data(t), nil)
132
+	ipdList := getIPv4Data(t)
133
+	ipd6List := []driverapi.IPAMData{
134
+		driverapi.IPAMData{
135
+			Pool: nwV6,
136
+			AuxAddresses: map[string]*net.IPNet{
137
+				DefaultGatewayV6AuxKey: gwV6,
138
+			},
139
+		},
140
+	}
141
+
142
+	err := d.CreateNetwork("dummy", netOption, ipdList, ipd6List)
126 143
 	if err != nil {
127 144
 		t.Fatalf("Failed to create bridge: %v", err)
128 145
 	}
... ...
@@ -132,7 +149,7 @@ func TestCreateFullOptionsLabels(t *testing.T) {
132 132
 		t.Fatalf("Cannot find dummy network in bridge driver")
133 133
 	}
134 134
 
135
-	if nw.config.BridgeName != "cu" {
135
+	if nw.config.BridgeName != DefaultBridgeName {
136 136
 		t.Fatalf("incongruent name in bridge network")
137 137
 	}
138 138
 
... ...
@@ -147,6 +164,36 @@ func TestCreateFullOptionsLabels(t *testing.T) {
147 147
 	if !nw.config.EnableIPMasquerade {
148 148
 		t.Fatalf("incongruent EnableIPMasquerade in bridge network")
149 149
 	}
150
+
151
+	bndIP := net.ParseIP(bndIPs)
152
+	if !bndIP.Equal(nw.config.DefaultBindingIP) {
153
+		t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP)
154
+	}
155
+
156
+	if !types.CompareIPNet(nw.config.AddressIPv6, nwV6) {
157
+		t.Fatalf("Unexpected: %v", nw.config.AddressIPv6)
158
+	}
159
+
160
+	if !gwV6.IP.Equal(nw.config.DefaultGatewayIPv6) {
161
+		t.Fatalf("Unexpected: %v", nw.config.DefaultGatewayIPv6)
162
+	}
163
+
164
+	// In short here we are testing --fixed-cidr-v6 daemon option
165
+	// plus --mac-address run option
166
+	mac, _ := net.ParseMAC("aa:bb:cc:dd:ee:ff")
167
+	epOptions := map[string]interface{}{netlabel.MacAddress: mac}
168
+	te := newTestEndpoint(ipdList[0].Pool, 20)
169
+	err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions)
170
+	if err != nil {
171
+		t.Fatal(err)
172
+	}
173
+
174
+	if !nwV6.Contains(te.Interface().AddressIPv6().IP) {
175
+		t.Fatalf("endpoint got assigned address outside of container network(%s): %s", nwV6.String(), te.Interface().AddressIPv6())
176
+	}
177
+	if te.Interface().AddressIPv6().IP.String() != "2100:2400:2600:2700:2800:aabb:ccdd:eeff" {
178
+		t.Fatalf("Unexpected endpoint IPv6 address: %v", te.Interface().AddressIPv6().IP)
179
+	}
150 180
 }
151 181
 
152 182
 func TestCreate(t *testing.T) {
... ...
@@ -737,7 +737,7 @@ func (ep *endpoint) DataScope() string {
737 737
 	return ep.getNetwork().DataScope()
738 738
 }
739 739
 
740
-func (ep *endpoint) assignAddress() error {
740
+func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error {
741 741
 	var (
742 742
 		ipam ipamapi.Ipam
743 743
 		err  error
... ...
@@ -754,11 +754,18 @@ func (ep *endpoint) assignAddress() error {
754 754
 	if err != nil {
755 755
 		return err
756 756
 	}
757
-	err = ep.assignAddressVersion(4, ipam)
758
-	if err != nil {
759
-		return err
757
+
758
+	if assignIPv4 {
759
+		if err = ep.assignAddressVersion(4, ipam); err != nil {
760
+			return err
761
+		}
760 762
 	}
761
-	return ep.assignAddressVersion(6, ipam)
763
+
764
+	if assignIPv6 {
765
+		err = ep.assignAddressVersion(6, ipam)
766
+	}
767
+
768
+	return err
762 769
 }
763 770
 
764 771
 func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
... ...
@@ -787,7 +794,11 @@ func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
787 787
 	}
788 788
 
789 789
 	for _, d := range ipInfo {
790
-		addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil)
790
+		var prefIP net.IP
791
+		if *address != nil {
792
+			prefIP = (*address).IP
793
+		}
794
+		addr, _, err := ipam.RequestAddress(d.PoolID, prefIP, nil)
791 795
 		if err == nil {
792 796
 			ep.Lock()
793 797
 			*address = addr
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"flag"
7 7
 	"fmt"
8 8
 	"io/ioutil"
9
+	"net"
9 10
 	"net/http"
10 11
 	"net/http/httptest"
11 12
 	"os"
... ...
@@ -313,6 +314,59 @@ func TestBridge(t *testing.T) {
313 313
 	}
314 314
 }
315 315
 
316
+// Testing IPV6 from MAC address
317
+func TestBridgeIpv6FromMac(t *testing.T) {
318
+	if !testutils.IsRunningInContainer() {
319
+		defer testutils.SetupTestOSContext(t)()
320
+	}
321
+
322
+	netOption := options.Generic{
323
+		netlabel.GenericData: options.Generic{
324
+			"BridgeName":         "testipv6mac",
325
+			"EnableIPv6":         true,
326
+			"EnableICC":          true,
327
+			"EnableIPMasquerade": true,
328
+		},
329
+	}
330
+	ipamV4ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "192.168.100.0/24", Gateway: "192.168.100.1"}}
331
+	ipamV6ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "fe90::/64", Gateway: "fe90::22"}}
332
+
333
+	network, err := controller.NewNetwork(bridgeNetType, "testipv6mac",
334
+		libnetwork.NetworkOptionGeneric(netOption),
335
+		libnetwork.NetworkOptionIpam(ipamapi.DefaultIPAM, "", ipamV4ConfList, ipamV6ConfList),
336
+		libnetwork.NetworkOptionDeferIPv6Alloc(true))
337
+	if err != nil {
338
+		t.Fatal(err)
339
+	}
340
+
341
+	mac := net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}
342
+	epOption := options.Generic{netlabel.MacAddress: mac}
343
+
344
+	ep, err := network.CreateEndpoint("testep", libnetwork.EndpointOptionGeneric(epOption))
345
+	if err != nil {
346
+		t.Fatal(err)
347
+	}
348
+
349
+	iface := ep.Info().Iface()
350
+	if !bytes.Equal(iface.MacAddress(), mac) {
351
+		t.Fatalf("Unexpected mac address: %v", iface.MacAddress())
352
+	}
353
+
354
+	ip, expIP, _ := net.ParseCIDR("fe90::aabb:ccdd:eeff/64")
355
+	expIP.IP = ip
356
+	if !types.CompareIPNet(expIP, iface.AddressIPv6()) {
357
+		t.Fatalf("Expected %v. Got: %v", expIP, iface.AddressIPv6())
358
+	}
359
+
360
+	if err := ep.Delete(); err != nil {
361
+		t.Fatal(err)
362
+	}
363
+
364
+	if err := network.Delete(); err != nil {
365
+		t.Fatal(err)
366
+	}
367
+}
368
+
316 369
 func TestUnknownDriver(t *testing.T) {
317 370
 	if !testutils.IsRunningInContainer() {
318 371
 		defer testutils.SetupTestOSContext(t)()
... ...
@@ -152,6 +152,7 @@ type network struct {
152 152
 	ipamV4Info   []*IpamInfo
153 153
 	ipamV6Info   []*IpamInfo
154 154
 	enableIPv6   bool
155
+	postIPv6     bool
155 156
 	epCnt        *endpointCnt
156 157
 	generic      options.Generic
157 158
 	dbIndex      uint64
... ...
@@ -298,6 +299,7 @@ func (n *network) CopyTo(o datastore.KVObject) error {
298 298
 	dstN.ipamType = n.ipamType
299 299
 	dstN.enableIPv6 = n.enableIPv6
300 300
 	dstN.persist = n.persist
301
+	dstN.postIPv6 = n.postIPv6
301 302
 	dstN.dbIndex = n.dbIndex
302 303
 	dstN.dbExists = n.dbExists
303 304
 	dstN.drvOnce = n.drvOnce
... ...
@@ -358,6 +360,7 @@ func (n *network) MarshalJSON() ([]byte, error) {
358 358
 		netMap["generic"] = n.generic
359 359
 	}
360 360
 	netMap["persist"] = n.persist
361
+	netMap["postIPv6"] = n.postIPv6
361 362
 	if len(n.ipamV4Config) > 0 {
362 363
 		ics, err := json.Marshal(n.ipamV4Config)
363 364
 		if err != nil {
... ...
@@ -418,6 +421,9 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
418 418
 	if v, ok := netMap["persist"]; ok {
419 419
 		n.persist = v.(bool)
420 420
 	}
421
+	if v, ok := netMap["postIPv6"]; ok {
422
+		n.postIPv6 = v.(bool)
423
+	}
421 424
 	if v, ok := netMap["ipamType"]; ok {
422 425
 		n.ipamType = v.(string)
423 426
 	} else {
... ...
@@ -505,6 +511,16 @@ func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
505 505
 	}
506 506
 }
507 507
 
508
+// NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
509
+// It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
510
+// to a container as combination of fixed-cidr-v6 + mac-address
511
+// TODO: Remove this option setter once we support endpoint ipam options
512
+func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
513
+	return func(n *network) {
514
+		n.postIPv6 = enable
515
+	}
516
+}
517
+
508 518
 func (n *network) processOptions(options ...NetworkOption) {
509 519
 	for _, opt := range options {
510 520
 		if opt != nil {
... ...
@@ -655,7 +671,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
655 655
 
656 656
 	ep.processOptions(options...)
657 657
 
658
-	if err = ep.assignAddress(); err != nil {
658
+	if err = ep.assignAddress(true, !n.postIPv6); err != nil {
659 659
 		return nil, err
660 660
 	}
661 661
 	defer func() {
... ...
@@ -675,6 +691,10 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
675 675
 		}
676 676
 	}()
677 677
 
678
+	if err = ep.assignAddress(false, n.postIPv6); err != nil {
679
+		return nil, err
680
+	}
681
+
678 682
 	if err = n.getController().updateToStore(ep); err != nil {
679 683
 		return nil, err
680 684
 	}