Skip to content

Commit e8ebc0b

Browse files
committed
Merge pull request #741 from aboch/b6
Allow IPv6 allocation post endpoint create via network option
2 parents 52d7e76 + 3254c6b commit e8ebc0b

5 files changed

Lines changed: 158 additions & 17 deletions

File tree

drivers/bridge/bridge.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
450450
}
451451

452452
if len(ipamV6Data) > 0 {
453+
c.AddressIPv6 = ipamV6Data[0].Pool
454+
453455
if ipamV6Data[0].Gateway != nil {
454456
c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
455457
}
@@ -961,13 +963,20 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
961963
if endpoint.addrv6 == nil && config.EnableIPv6 {
962964
var ip6 net.IP
963965
network := n.bridge.bridgeIPv6
966+
if config.AddressIPv6 != nil {
967+
network = config.AddressIPv6
968+
}
969+
964970
ones, _ := network.Mask.Size()
965-
if ones <= 80 {
966-
ip6 = make(net.IP, len(network.IP))
967-
copy(ip6, network.IP)
968-
for i, h := range endpoint.macAddress {
969-
ip6[i+10] = h
970-
}
971+
if ones > 80 {
972+
err = types.ForbiddenErrorf("Cannot self generate an IPv6 address on network %v: At least 48 host bits are needed.", network)
973+
return err
974+
}
975+
976+
ip6 = make(net.IP, len(network.IP))
977+
copy(ip6, network.IP)
978+
for i, h := range endpoint.macAddress {
979+
ip6[i+10] = h
971980
}
972981

973982
endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}

drivers/bridge/bridge_test.go

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,18 +111,35 @@ func TestCreateFullOptionsLabels(t *testing.T) {
111111
t.Fatalf("Failed to setup driver config: %v", err)
112112
}
113113

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+
114120
labels := map[string]string{
115-
BridgeName: "cu",
121+
BridgeName: DefaultBridgeName,
122+
DefaultBridge: "true",
116123
netlabel.EnableIPv6: "true",
117124
EnableICC: "true",
118125
EnableIPMasquerade: "true",
119-
DefaultBindingIP: "127.0.0.1",
126+
DefaultBindingIP: bndIPs,
120127
}
121128

122129
netOption := make(map[string]interface{})
123130
netOption[netlabel.GenericData] = labels
124131

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)
126143
if err != nil {
127144
t.Fatalf("Failed to create bridge: %v", err)
128145
}
@@ -132,7 +149,7 @@ func TestCreateFullOptionsLabels(t *testing.T) {
132149
t.Fatalf("Cannot find dummy network in bridge driver")
133150
}
134151

135-
if nw.config.BridgeName != "cu" {
152+
if nw.config.BridgeName != DefaultBridgeName {
136153
t.Fatalf("incongruent name in bridge network")
137154
}
138155

@@ -147,6 +164,36 @@ func TestCreateFullOptionsLabels(t *testing.T) {
147164
if !nw.config.EnableIPMasquerade {
148165
t.Fatalf("incongruent EnableIPMasquerade in bridge network")
149166
}
167+
168+
bndIP := net.ParseIP(bndIPs)
169+
if !bndIP.Equal(nw.config.DefaultBindingIP) {
170+
t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP)
171+
}
172+
173+
if !types.CompareIPNet(nw.config.AddressIPv6, nwV6) {
174+
t.Fatalf("Unexpected: %v", nw.config.AddressIPv6)
175+
}
176+
177+
if !gwV6.IP.Equal(nw.config.DefaultGatewayIPv6) {
178+
t.Fatalf("Unexpected: %v", nw.config.DefaultGatewayIPv6)
179+
}
180+
181+
// In short here we are testing --fixed-cidr-v6 daemon option
182+
// plus --mac-address run option
183+
mac, _ := net.ParseMAC("aa:bb:cc:dd:ee:ff")
184+
epOptions := map[string]interface{}{netlabel.MacAddress: mac}
185+
te := newTestEndpoint(ipdList[0].Pool, 20)
186+
err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions)
187+
if err != nil {
188+
t.Fatal(err)
189+
}
190+
191+
if !nwV6.Contains(te.Interface().AddressIPv6().IP) {
192+
t.Fatalf("endpoint got assigned address outside of container network(%s): %s", nwV6.String(), te.Interface().AddressIPv6())
193+
}
194+
if te.Interface().AddressIPv6().IP.String() != "2100:2400:2600:2700:2800:aabb:ccdd:eeff" {
195+
t.Fatalf("Unexpected endpoint IPv6 address: %v", te.Interface().AddressIPv6().IP)
196+
}
150197
}
151198

152199
func TestCreate(t *testing.T) {

endpoint.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ func (ep *endpoint) DataScope() string {
737737
return ep.getNetwork().DataScope()
738738
}
739739

740-
func (ep *endpoint) assignAddress() error {
740+
func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error {
741741
var (
742742
ipam ipamapi.Ipam
743743
err error
@@ -754,11 +754,18 @@ func (ep *endpoint) assignAddress() error {
754754
if err != nil {
755755
return err
756756
}
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+
}
760762
}
761-
return ep.assignAddressVersion(6, ipam)
763+
764+
if assignIPv6 {
765+
err = ep.assignAddressVersion(6, ipam)
766+
}
767+
768+
return err
762769
}
763770

764771
func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
@@ -787,7 +794,11 @@ func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
787794
}
788795

789796
for _, d := range ipInfo {
790-
addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil)
797+
var prefIP net.IP
798+
if *address != nil {
799+
prefIP = (*address).IP
800+
}
801+
addr, _, err := ipam.RequestAddress(d.PoolID, prefIP, nil)
791802
if err == nil {
792803
ep.Lock()
793804
*address = addr

libnetwork_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"flag"
77
"fmt"
88
"io/ioutil"
9+
"net"
910
"net/http"
1011
"net/http/httptest"
1112
"os"
@@ -313,6 +314,59 @@ func TestBridge(t *testing.T) {
313314
}
314315
}
315316

317+
// Testing IPV6 from MAC address
318+
func TestBridgeIpv6FromMac(t *testing.T) {
319+
if !testutils.IsRunningInContainer() {
320+
defer testutils.SetupTestOSContext(t)()
321+
}
322+
323+
netOption := options.Generic{
324+
netlabel.GenericData: options.Generic{
325+
"BridgeName": "testipv6mac",
326+
"EnableIPv6": true,
327+
"EnableICC": true,
328+
"EnableIPMasquerade": true,
329+
},
330+
}
331+
ipamV4ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "192.168.100.0/24", Gateway: "192.168.100.1"}}
332+
ipamV6ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "fe90::/64", Gateway: "fe90::22"}}
333+
334+
network, err := controller.NewNetwork(bridgeNetType, "testipv6mac",
335+
libnetwork.NetworkOptionGeneric(netOption),
336+
libnetwork.NetworkOptionIpam(ipamapi.DefaultIPAM, "", ipamV4ConfList, ipamV6ConfList),
337+
libnetwork.NetworkOptionDeferIPv6Alloc(true))
338+
if err != nil {
339+
t.Fatal(err)
340+
}
341+
342+
mac := net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}
343+
epOption := options.Generic{netlabel.MacAddress: mac}
344+
345+
ep, err := network.CreateEndpoint("testep", libnetwork.EndpointOptionGeneric(epOption))
346+
if err != nil {
347+
t.Fatal(err)
348+
}
349+
350+
iface := ep.Info().Iface()
351+
if !bytes.Equal(iface.MacAddress(), mac) {
352+
t.Fatalf("Unexpected mac address: %v", iface.MacAddress())
353+
}
354+
355+
ip, expIP, _ := net.ParseCIDR("fe90::aabb:ccdd:eeff/64")
356+
expIP.IP = ip
357+
if !types.CompareIPNet(expIP, iface.AddressIPv6()) {
358+
t.Fatalf("Expected %v. Got: %v", expIP, iface.AddressIPv6())
359+
}
360+
361+
if err := ep.Delete(); err != nil {
362+
t.Fatal(err)
363+
}
364+
365+
if err := network.Delete(); err != nil {
366+
t.Fatal(err)
367+
}
368+
}
369+
316370
func TestUnknownDriver(t *testing.T) {
317371
if !testutils.IsRunningInContainer() {
318372
defer testutils.SetupTestOSContext(t)()

network.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ type network struct {
152152
ipamV4Info []*IpamInfo
153153
ipamV6Info []*IpamInfo
154154
enableIPv6 bool
155+
postIPv6 bool
155156
epCnt *endpointCnt
156157
generic options.Generic
157158
dbIndex uint64
@@ -298,6 +299,7 @@ func (n *network) CopyTo(o datastore.KVObject) error {
298299
dstN.ipamType = n.ipamType
299300
dstN.enableIPv6 = n.enableIPv6
300301
dstN.persist = n.persist
302+
dstN.postIPv6 = n.postIPv6
301303
dstN.dbIndex = n.dbIndex
302304
dstN.dbExists = n.dbExists
303305
dstN.drvOnce = n.drvOnce
@@ -358,6 +360,7 @@ func (n *network) MarshalJSON() ([]byte, error) {
358360
netMap["generic"] = n.generic
359361
}
360362
netMap["persist"] = n.persist
363+
netMap["postIPv6"] = n.postIPv6
361364
if len(n.ipamV4Config) > 0 {
362365
ics, err := json.Marshal(n.ipamV4Config)
363366
if err != nil {
@@ -418,6 +421,9 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
418421
if v, ok := netMap["persist"]; ok {
419422
n.persist = v.(bool)
420423
}
424+
if v, ok := netMap["postIPv6"]; ok {
425+
n.postIPv6 = v.(bool)
426+
}
421427
if v, ok := netMap["ipamType"]; ok {
422428
n.ipamType = v.(string)
423429
} else {
@@ -505,6 +511,16 @@ func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
505511
}
506512
}
507513

514+
// NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
515+
// It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
516+
// to a container as combination of fixed-cidr-v6 + mac-address
517+
// TODO: Remove this option setter once we support endpoint ipam options
518+
func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
519+
return func(n *network) {
520+
n.postIPv6 = enable
521+
}
522+
}
523+
508524
func (n *network) processOptions(options ...NetworkOption) {
509525
for _, opt := range options {
510526
if opt != nil {
@@ -655,7 +671,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
655671

656672
ep.processOptions(options...)
657673

658-
if err = ep.assignAddress(); err != nil {
674+
if err = ep.assignAddress(true, !n.postIPv6); err != nil {
659675
return nil, err
660676
}
661677
defer func() {
@@ -675,6 +691,10 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
675691
}
676692
}()
677693

694+
if err = ep.assignAddress(false, n.postIPv6); err != nil {
695+
return nil, err
696+
}
697+
678698
if err = n.getController().updateToStore(ep); err != nil {
679699
return nil, err
680700
}

0 commit comments

Comments
 (0)