Browse Source

Merge pull request #1514 from puneetpruthi/overlay_support

Overlay driver support for Solaris
Alessandro Boch 8 years ago
parent
commit
734f4ec86d

+ 21 - 2
libnetwork/default_gateway_solaris.go

@@ -1,7 +1,26 @@
 package libnetwork
 
-import "github.com/docker/libnetwork/types"
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/docker/libnetwork/drivers/solaris/bridge"
+)
 
 func (c *controller) createGWNetwork() (Network, error) {
-	return nil, types.NotImplementedErrorf("default gateway functionality is not implemented in solaris")
+	netOption := map[string]string{
+		bridge.BridgeName:         libnGWNetwork,
+		bridge.EnableICC:          strconv.FormatBool(false),
+		bridge.EnableIPMasquerade: strconv.FormatBool(true),
+	}
+
+	n, err := c.NewNetwork("bridge", libnGWNetwork, "",
+		NetworkOptionDriverOpts(netOption),
+		NetworkOptionEnableIPv6(false),
+	)
+
+	if err != nil {
+		return nil, fmt.Errorf("error creating external connectivity network: %v", err)
+	}
+	return n, err
 }

+ 274 - 0
libnetwork/drivers/solaris/overlay/encryption.go

@@ -0,0 +1,274 @@
+package overlay
+
+import (
+	"bytes"
+	"encoding/binary"
+	"encoding/hex"
+	"fmt"
+	"hash/fnv"
+	"net"
+	"sync"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/types"
+)
+
+const (
+	mark         = uint32(0xD0C4E3)
+	timeout      = 30
+	pktExpansion = 26 // SPI(4) + SeqN(4) + IV(8) + PadLength(1) + NextHeader(1) + ICV(8)
+)
+
+const (
+	forward = iota + 1
+	reverse
+	bidir
+)
+
+type key struct {
+	value []byte
+	tag   uint32
+}
+
+func (k *key) String() string {
+	if k != nil {
+		return fmt.Sprintf("(key: %s, tag: 0x%x)", hex.EncodeToString(k.value)[0:5], k.tag)
+	}
+	return ""
+}
+
+type spi struct {
+	forward int
+	reverse int
+}
+
+func (s *spi) String() string {
+	return fmt.Sprintf("SPI(FWD: 0x%x, REV: 0x%x)", uint32(s.forward), uint32(s.reverse))
+}
+
+type encrMap struct {
+	nodes map[string][]*spi
+	sync.Mutex
+}
+
+func (e *encrMap) String() string {
+	e.Lock()
+	defer e.Unlock()
+	b := new(bytes.Buffer)
+	for k, v := range e.nodes {
+		b.WriteString("\n")
+		b.WriteString(k)
+		b.WriteString(":")
+		b.WriteString("[")
+		for _, s := range v {
+			b.WriteString(s.String())
+			b.WriteString(",")
+		}
+		b.WriteString("]")
+
+	}
+	return b.String()
+}
+
+func (d *driver) checkEncryption(nid string, rIP net.IP, vxlanID uint32, isLocal, add bool) error {
+	log.Debugf("checkEncryption(%s, %v, %d, %t)", nid[0:7], rIP, vxlanID, isLocal)
+
+	n := d.network(nid)
+	if n == nil || !n.secure {
+		return nil
+	}
+
+	if len(d.keys) == 0 {
+		return types.ForbiddenErrorf("encryption key is not present")
+	}
+
+	lIP := net.ParseIP(d.bindAddress)
+	aIP := net.ParseIP(d.advertiseAddress)
+	nodes := map[string]net.IP{}
+
+	switch {
+	case isLocal:
+		if err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
+			if !aIP.Equal(pEntry.vtep) {
+				nodes[pEntry.vtep.String()] = pEntry.vtep
+			}
+			return false
+		}); err != nil {
+			log.Warnf("Failed to retrieve list of participating nodes in overlay network %s: %v", nid[0:5], err)
+		}
+	default:
+		if len(d.network(nid).endpoints) > 0 {
+			nodes[rIP.String()] = rIP
+		}
+	}
+
+	log.Debugf("List of nodes: %s", nodes)
+
+	if add {
+		for _, rIP := range nodes {
+			if err := setupEncryption(lIP, aIP, rIP, vxlanID, d.secMap, d.keys); err != nil {
+				log.Warnf("Failed to program network encryption between %s and %s: %v", lIP, rIP, err)
+			}
+		}
+	} else {
+		if len(nodes) == 0 {
+			if err := removeEncryption(lIP, rIP, d.secMap); err != nil {
+				log.Warnf("Failed to remove network encryption between %s and %s: %v", lIP, rIP, err)
+			}
+		}
+	}
+
+	return nil
+}
+
+func setupEncryption(localIP, advIP, remoteIP net.IP, vni uint32, em *encrMap, keys []*key) error {
+	log.Debugf("Programming encryption for vxlan %d between %s and %s", vni, localIP, remoteIP)
+	rIPs := remoteIP.String()
+
+	indices := make([]*spi, 0, len(keys))
+
+	err := programMangle(vni, true)
+	if err != nil {
+		log.Warn(err)
+	}
+
+	em.Lock()
+	em.nodes[rIPs] = indices
+	em.Unlock()
+
+	return nil
+}
+
+func removeEncryption(localIP, remoteIP net.IP, em *encrMap) error {
+	return nil
+}
+
+func programMangle(vni uint32, add bool) (err error) {
+	return
+}
+
+func buildSPI(src, dst net.IP, st uint32) int {
+	b := make([]byte, 4)
+	binary.BigEndian.PutUint32(b, st)
+	h := fnv.New32a()
+	h.Write(src)
+	h.Write(b)
+	h.Write(dst)
+	return int(binary.BigEndian.Uint32(h.Sum(nil)))
+}
+
+func (d *driver) secMapWalk(f func(string, []*spi) ([]*spi, bool)) error {
+	d.secMap.Lock()
+	for node, indices := range d.secMap.nodes {
+		idxs, stop := f(node, indices)
+		if idxs != nil {
+			d.secMap.nodes[node] = idxs
+		}
+		if stop {
+			break
+		}
+	}
+	d.secMap.Unlock()
+	return nil
+}
+
+func (d *driver) setKeys(keys []*key) error {
+	if d.keys != nil {
+		return types.ForbiddenErrorf("initial keys are already present")
+	}
+	d.keys = keys
+	log.Debugf("Initial encryption keys: %v", d.keys)
+	return nil
+}
+
+// updateKeys allows to add a new key and/or change the primary key and/or prune an existing key
+// The primary key is the key used in transmission and will go in first position in the list.
+func (d *driver) updateKeys(newKey, primary, pruneKey *key) error {
+	log.Debugf("Updating Keys. New: %v, Primary: %v, Pruned: %v", newKey, primary, pruneKey)
+
+	log.Debugf("Current: %v", d.keys)
+
+	var (
+		newIdx = -1
+		priIdx = -1
+		delIdx = -1
+		lIP    = net.ParseIP(d.bindAddress)
+	)
+
+	d.Lock()
+	// add new
+	if newKey != nil {
+		d.keys = append(d.keys, newKey)
+		newIdx += len(d.keys)
+	}
+	for i, k := range d.keys {
+		if primary != nil && k.tag == primary.tag {
+			priIdx = i
+		}
+		if pruneKey != nil && k.tag == pruneKey.tag {
+			delIdx = i
+		}
+	}
+	d.Unlock()
+
+	if (newKey != nil && newIdx == -1) ||
+		(primary != nil && priIdx == -1) ||
+		(pruneKey != nil && delIdx == -1) {
+		err := types.BadRequestErrorf("cannot find proper key indices while processing key update:"+
+			"(newIdx,priIdx,delIdx):(%d, %d, %d)", newIdx, priIdx, delIdx)
+		log.Warn(err)
+		return err
+	}
+
+	d.secMapWalk(func(rIPs string, spis []*spi) ([]*spi, bool) {
+		rIP := net.ParseIP(rIPs)
+		return updateNodeKey(lIP, rIP, spis, d.keys, newIdx, priIdx, delIdx), false
+	})
+
+	d.Lock()
+	// swap primary
+	if priIdx != -1 {
+		swp := d.keys[0]
+		d.keys[0] = d.keys[priIdx]
+		d.keys[priIdx] = swp
+	}
+	// prune
+	if delIdx != -1 {
+		if delIdx == 0 {
+			delIdx = priIdx
+		}
+		d.keys = append(d.keys[:delIdx], d.keys[delIdx+1:]...)
+	}
+	d.Unlock()
+
+	log.Debugf("Updated: %v", d.keys)
+
+	return nil
+}
+
+/********************************************************
+ * Steady state: rSA0, rSA1, rSA2, fSA1, fSP1
+ * Rotation --> -rSA0, +rSA3, +fSA2, +fSP2/-fSP1, -fSA1
+ * Steady state: rSA1, rSA2, rSA3, fSA2, fSP2
+ *********************************************************/
+
+// Spis and keys are sorted in such away the one in position 0 is the primary
+func updateNodeKey(lIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, priIdx, delIdx int) []*spi {
+	log.Debugf("Updating keys for node: %s (%d,%d,%d)", rIP, newIdx, priIdx, delIdx)
+	return nil
+}
+
+func (n *network) maxMTU() int {
+	mtu := 1500
+	if n.mtu != 0 {
+		mtu = n.mtu
+	}
+	mtu -= vxlanEncap
+	if n.secure {
+		// In case of encryption account for the
+		// esp packet espansion and padding
+		mtu -= pktExpansion
+		mtu -= (mtu % 4)
+	}
+	return mtu
+}

+ 184 - 0
libnetwork/drivers/solaris/overlay/joinleave.go

@@ -0,0 +1,184 @@
+package overlay
+
+import (
+	"fmt"
+	"net"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/types"
+	"github.com/gogo/protobuf/proto"
+)
+
+// Join method is invoked when a Sandbox is attached to an endpoint.
+func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
+	if err := validateID(nid, eid); err != nil {
+		return err
+	}
+
+	n := d.network(nid)
+	if n == nil {
+		return fmt.Errorf("could not find network with id %s", nid)
+	}
+
+	ep := n.endpoint(eid)
+	if ep == nil {
+		return fmt.Errorf("could not find endpoint with id %s", eid)
+	}
+
+	if n.secure && len(d.keys) == 0 {
+		return fmt.Errorf("cannot join secure network: encryption keys not present")
+	}
+
+	s := n.getSubnetforIP(ep.addr)
+	if s == nil {
+		return fmt.Errorf("could not find subnet for endpoint %s", eid)
+	}
+
+	if err := n.obtainVxlanID(s); err != nil {
+		return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
+	}
+
+	if err := n.joinSandbox(false); err != nil {
+		return fmt.Errorf("network sandbox join failed: %v", err)
+	}
+
+	if err := n.joinSubnetSandbox(s, false); err != nil {
+		return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
+	}
+
+	// joinSubnetSandbox gets called when an endpoint comes up on a new subnet in the
+	// overlay network. Hence the Endpoint count should be updated outside joinSubnetSandbox
+	n.incEndpointCount()
+
+	// Add creating a veth Pair for Solaris
+
+	containerIfName := "solaris-if"
+	ep.ifName = containerIfName
+
+	if err := d.writeEndpointToStore(ep); err != nil {
+		return fmt.Errorf("failed to update overlay endpoint %s to local data store: %v", ep.id[0:7], err)
+	}
+
+	// Add solaris plumbing to add veth (with ep mac addr) to sandbox
+
+	for _, sub := range n.subnets {
+		if sub == s {
+			continue
+		}
+		if err := jinfo.AddStaticRoute(sub.subnetIP, types.NEXTHOP, s.gwIP.IP); err != nil {
+			log.Errorf("Adding subnet %s static route in network %q failed\n", s.subnetIP, n.id)
+		}
+	}
+
+	if iNames := jinfo.InterfaceName(); iNames != nil {
+		err := iNames.SetNames(containerIfName, "eth")
+		if err != nil {
+			return err
+		}
+	}
+
+	d.peerDbAdd(nid, eid, ep.addr.IP, ep.addr.Mask, ep.mac,
+		net.ParseIP(d.advertiseAddress), true)
+
+	if err := d.checkEncryption(nid, nil, n.vxlanID(s), true, true); err != nil {
+		log.Warn(err)
+	}
+
+	buf, err := proto.Marshal(&PeerRecord{
+		EndpointIP:       ep.addr.String(),
+		EndpointMAC:      ep.mac.String(),
+		TunnelEndpointIP: d.advertiseAddress,
+	})
+	if err != nil {
+		return err
+	}
+
+	if err := jinfo.AddTableEntry(ovPeerTable, eid, buf); err != nil {
+		log.Errorf("overlay: Failed adding table entry to joininfo: %v", err)
+	}
+
+	d.pushLocalEndpointEvent("join", nid, eid)
+
+	return nil
+}
+
+func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
+	if tableName != ovPeerTable {
+		log.Errorf("Unexpected table notification for table %s received", tableName)
+		return
+	}
+
+	eid := key
+
+	var peer PeerRecord
+	if err := proto.Unmarshal(value, &peer); err != nil {
+		log.Errorf("Failed to unmarshal peer record: %v", err)
+		return
+	}
+
+	// Ignore local peers. We already know about them and they
+	// should not be added to vxlan fdb.
+	if peer.TunnelEndpointIP == d.advertiseAddress {
+		return
+	}
+
+	addr, err := types.ParseCIDR(peer.EndpointIP)
+	if err != nil {
+		log.Errorf("Invalid peer IP %s received in event notify", peer.EndpointIP)
+		return
+	}
+
+	mac, err := net.ParseMAC(peer.EndpointMAC)
+	if err != nil {
+		log.Errorf("Invalid mac %s received in event notify", peer.EndpointMAC)
+		return
+	}
+
+	vtep := net.ParseIP(peer.TunnelEndpointIP)
+	if vtep == nil {
+		log.Errorf("Invalid VTEP %s received in event notify", peer.TunnelEndpointIP)
+		return
+	}
+
+	if etype == driverapi.Delete {
+		d.peerDelete(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
+		return
+	}
+
+	d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
+}
+
+// Leave method is invoked when a Sandbox detaches from an endpoint.
+func (d *driver) Leave(nid, eid string) error {
+	if err := validateID(nid, eid); err != nil {
+		return err
+	}
+
+	n := d.network(nid)
+	if n == nil {
+		return fmt.Errorf("could not find network with id %s", nid)
+	}
+
+	ep := n.endpoint(eid)
+
+	if ep == nil {
+		return types.InternalMaskableErrorf("could not find endpoint with id %s", eid)
+	}
+
+	if d.notifyCh != nil {
+		d.notifyCh <- ovNotify{
+			action: "leave",
+			nw:     n,
+			ep:     ep,
+		}
+	}
+
+	n.leaveSandbox()
+
+	if err := d.checkEncryption(nid, nil, 0, true, false); err != nil {
+		log.Warn(err)
+	}
+
+	return nil
+}

+ 249 - 0
libnetwork/drivers/solaris/overlay/ov_endpoint.go

@@ -0,0 +1,249 @@
+package overlay
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/datastore"
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/types"
+)
+
+type endpointTable map[string]*endpoint
+
+const overlayEndpointPrefix = "overlay/endpoint"
+
+type endpoint struct {
+	id       string
+	nid      string
+	ifName   string
+	mac      net.HardwareAddr
+	addr     *net.IPNet
+	dbExists bool
+	dbIndex  uint64
+}
+
+func (n *network) endpoint(eid string) *endpoint {
+	n.Lock()
+	defer n.Unlock()
+
+	return n.endpoints[eid]
+}
+
+func (n *network) addEndpoint(ep *endpoint) {
+	n.Lock()
+	n.endpoints[ep.id] = ep
+	n.Unlock()
+}
+
+func (n *network) deleteEndpoint(eid string) {
+	n.Lock()
+	delete(n.endpoints, eid)
+	n.Unlock()
+}
+
+func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
+	epOptions map[string]interface{}) error {
+	var err error
+
+	if err = validateID(nid, eid); err != nil {
+		return err
+	}
+
+	// Since we perform lazy configuration make sure we try
+	// configuring the driver when we enter CreateEndpoint since
+	// CreateNetwork may not be called in every node.
+	if err := d.configure(); err != nil {
+		return err
+	}
+
+	n := d.network(nid)
+	if n == nil {
+		return fmt.Errorf("network id %q not found", nid)
+	}
+
+	ep := &endpoint{
+		id:   eid,
+		nid:  n.id,
+		addr: ifInfo.Address(),
+		mac:  ifInfo.MacAddress(),
+	}
+	if ep.addr == nil {
+		return fmt.Errorf("create endpoint was not passed interface IP address")
+	}
+
+	if s := n.getSubnetforIP(ep.addr); s == nil {
+		return fmt.Errorf("no matching subnet for IP %q in network %q\n", ep.addr, nid)
+	}
+
+	if ep.mac == nil {
+		ep.mac = netutils.GenerateMACFromIP(ep.addr.IP)
+		if err := ifInfo.SetMacAddress(ep.mac); err != nil {
+			return err
+		}
+	}
+
+	n.addEndpoint(ep)
+
+	if err := d.writeEndpointToStore(ep); err != nil {
+		return fmt.Errorf("failed to update overlay endpoint %s to local store: %v", ep.id[0:7], err)
+	}
+
+	return nil
+}
+
+func (d *driver) DeleteEndpoint(nid, eid string) error {
+	if err := validateID(nid, eid); err != nil {
+		return err
+	}
+
+	n := d.network(nid)
+	if n == nil {
+		return fmt.Errorf("network id %q not found", nid)
+	}
+
+	ep := n.endpoint(eid)
+	if ep == nil {
+		return fmt.Errorf("endpoint id %q not found", eid)
+	}
+
+	n.deleteEndpoint(eid)
+
+	if err := d.deleteEndpointFromStore(ep); err != nil {
+		log.Warnf("Failed to delete overlay endpoint %s from local store: %v", ep.id[0:7], err)
+	}
+
+	if ep.ifName == "" {
+		return nil
+	}
+
+	// OVERLAY_SOLARIS: Add Solaris unplumbing for removing the interface endpoint
+
+	return nil
+}
+
+func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
+	return make(map[string]interface{}, 0), nil
+}
+
+func (d *driver) deleteEndpointFromStore(e *endpoint) error {
+	if d.localStore == nil {
+		return fmt.Errorf("overlay local store not initialized, ep not deleted")
+	}
+
+	if err := d.localStore.DeleteObjectAtomic(e); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (d *driver) writeEndpointToStore(e *endpoint) error {
+	if d.localStore == nil {
+		return fmt.Errorf("overlay local store not initialized, ep not added")
+	}
+
+	if err := d.localStore.PutObjectAtomic(e); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (ep *endpoint) DataScope() string {
+	return datastore.LocalScope
+}
+
+func (ep *endpoint) New() datastore.KVObject {
+	return &endpoint{}
+}
+
+func (ep *endpoint) CopyTo(o datastore.KVObject) error {
+	dstep := o.(*endpoint)
+	*dstep = *ep
+	return nil
+}
+
+func (ep *endpoint) Key() []string {
+	return []string{overlayEndpointPrefix, ep.id}
+}
+
+func (ep *endpoint) KeyPrefix() []string {
+	return []string{overlayEndpointPrefix}
+}
+
+func (ep *endpoint) Index() uint64 {
+	return ep.dbIndex
+}
+
+func (ep *endpoint) SetIndex(index uint64) {
+	ep.dbIndex = index
+	ep.dbExists = true
+}
+
+func (ep *endpoint) Exists() bool {
+	return ep.dbExists
+}
+
+func (ep *endpoint) Skip() bool {
+	return false
+}
+
+func (ep *endpoint) Value() []byte {
+	b, err := json.Marshal(ep)
+	if err != nil {
+		return nil
+	}
+	return b
+}
+
+func (ep *endpoint) SetValue(value []byte) error {
+	return json.Unmarshal(value, ep)
+}
+
+func (ep *endpoint) MarshalJSON() ([]byte, error) {
+	epMap := make(map[string]interface{})
+
+	epMap["id"] = ep.id
+	epMap["nid"] = ep.nid
+	if ep.ifName != "" {
+		epMap["ifName"] = ep.ifName
+	}
+	if ep.addr != nil {
+		epMap["addr"] = ep.addr.String()
+	}
+	if len(ep.mac) != 0 {
+		epMap["mac"] = ep.mac.String()
+	}
+
+	return json.Marshal(epMap)
+}
+
+func (ep *endpoint) UnmarshalJSON(value []byte) error {
+	var (
+		err   error
+		epMap map[string]interface{}
+	)
+
+	json.Unmarshal(value, &epMap)
+
+	ep.id = epMap["id"].(string)
+	ep.nid = epMap["nid"].(string)
+	if v, ok := epMap["mac"]; ok {
+		if ep.mac, err = net.ParseMAC(v.(string)); err != nil {
+			return types.InternalErrorf("failed to decode endpoint interface mac address after json unmarshal: %s", v.(string))
+		}
+	}
+	if v, ok := epMap["addr"]; ok {
+		if ep.addr, err = types.ParseCIDR(v.(string)); err != nil {
+			return types.InternalErrorf("failed to decode endpoint interface ipv4 address after json unmarshal: %v", err)
+		}
+	}
+	if v, ok := epMap["ifName"]; ok {
+		ep.ifName = v.(string)
+	}
+
+	return nil
+}

+ 791 - 0
libnetwork/drivers/solaris/overlay/ov_network.go

@@ -0,0 +1,791 @@
+package overlay
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/datastore"
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/netlabel"
+	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/osl"
+	"github.com/docker/libnetwork/resolvconf"
+	"github.com/docker/libnetwork/types"
+)
+
+var (
+	hostMode    bool
+	networkOnce sync.Once
+	networkMu   sync.Mutex
+	vniTbl      = make(map[uint32]string)
+)
+
+type networkTable map[string]*network
+
+type subnet struct {
+	once      *sync.Once
+	vxlanName string
+	brName    string
+	vni       uint32
+	initErr   error
+	subnetIP  *net.IPNet
+	gwIP      *net.IPNet
+}
+
+type subnetJSON struct {
+	SubnetIP string
+	GwIP     string
+	Vni      uint32
+}
+
+type network struct {
+	id        string
+	dbIndex   uint64
+	dbExists  bool
+	sbox      osl.Sandbox
+	endpoints endpointTable
+	driver    *driver
+	joinCnt   int
+	once      *sync.Once
+	initEpoch int
+	initErr   error
+	subnets   []*subnet
+	secure    bool
+	mtu       int
+	sync.Mutex
+}
+
+func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
+	return nil, types.NotImplementedErrorf("not implemented")
+}
+
+func (d *driver) NetworkFree(id string) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
+	if id == "" {
+		return fmt.Errorf("invalid network id")
+	}
+	if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
+		return types.BadRequestErrorf("ipv4 pool is empty")
+	}
+
+	// Since we perform lazy configuration make sure we try
+	// configuring the driver when we enter CreateNetwork
+	if err := d.configure(); err != nil {
+		return err
+	}
+
+	n := &network{
+		id:        id,
+		driver:    d,
+		endpoints: endpointTable{},
+		once:      &sync.Once{},
+		subnets:   []*subnet{},
+	}
+
+	vnis := make([]uint32, 0, len(ipV4Data))
+	if gval, ok := option[netlabel.GenericData]; ok {
+		optMap := gval.(map[string]string)
+		if val, ok := optMap[netlabel.OverlayVxlanIDList]; ok {
+			logrus.Debugf("overlay: Received vxlan IDs: %s", val)
+			vniStrings := strings.Split(val, ",")
+			for _, vniStr := range vniStrings {
+				vni, err := strconv.Atoi(vniStr)
+				if err != nil {
+					return fmt.Errorf("invalid vxlan id value %q passed", vniStr)
+				}
+
+				vnis = append(vnis, uint32(vni))
+			}
+		}
+		if _, ok := optMap[secureOption]; ok {
+			n.secure = true
+		}
+		if val, ok := optMap[netlabel.DriverMTU]; ok {
+			var err error
+			if n.mtu, err = strconv.Atoi(val); err != nil {
+				return fmt.Errorf("failed to parse %v: %v", val, err)
+			}
+			if n.mtu < 0 {
+				return fmt.Errorf("invalid MTU value: %v", n.mtu)
+			}
+		}
+	}
+
+	// If we are getting vnis from libnetwork, either we get for
+	// all subnets or none.
+	if len(vnis) != 0 && len(vnis) < len(ipV4Data) {
+		return fmt.Errorf("insufficient vnis(%d) passed to overlay", len(vnis))
+	}
+
+	for i, ipd := range ipV4Data {
+		s := &subnet{
+			subnetIP: ipd.Pool,
+			gwIP:     ipd.Gateway,
+			once:     &sync.Once{},
+		}
+
+		if len(vnis) != 0 {
+			s.vni = vnis[i]
+		}
+
+		n.subnets = append(n.subnets, s)
+	}
+
+	if err := n.writeToStore(); err != nil {
+		return fmt.Errorf("failed to update data store for network %v: %v", n.id, err)
+	}
+
+	// Make sure no rule is on the way from any stale secure network
+	if !n.secure {
+		for _, vni := range vnis {
+			programMangle(vni, false)
+		}
+	}
+
+	if nInfo != nil {
+		if err := nInfo.TableEventRegister(ovPeerTable); err != nil {
+			return err
+		}
+	}
+
+	d.addNetwork(n)
+	return nil
+}
+
+func (d *driver) DeleteNetwork(nid string) error {
+	if nid == "" {
+		return fmt.Errorf("invalid network id")
+	}
+
+	// Make sure driver resources are initialized before proceeding
+	if err := d.configure(); err != nil {
+		return err
+	}
+
+	n := d.network(nid)
+	if n == nil {
+		return fmt.Errorf("could not find network with id %s", nid)
+	}
+
+	d.deleteNetwork(nid)
+
+	vnis, err := n.releaseVxlanID()
+	if err != nil {
+		return err
+	}
+
+	if n.secure {
+		for _, vni := range vnis {
+			programMangle(vni, false)
+		}
+	}
+
+	return nil
+}
+
+func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
+	return nil
+}
+
+func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
+	return nil
+}
+
+func (n *network) incEndpointCount() {
+	n.Lock()
+	defer n.Unlock()
+	n.joinCnt++
+}
+
+func (n *network) joinSandbox(restore bool) error {
+	// If there is a race between two go routines here only one will win
+	// the other will wait.
+	n.once.Do(func() {
+		// save the error status of initSandbox in n.initErr so that
+		// all the racing go routines are able to know the status.
+		n.initErr = n.initSandbox(restore)
+	})
+
+	return n.initErr
+}
+
+func (n *network) joinSubnetSandbox(s *subnet, restore bool) error {
+	s.once.Do(func() {
+		s.initErr = n.initSubnetSandbox(s, restore)
+	})
+	return s.initErr
+}
+
+func (n *network) leaveSandbox() {
+	n.Lock()
+	defer n.Unlock()
+	n.joinCnt--
+	if n.joinCnt != 0 {
+		return
+	}
+
+	// We are about to destroy sandbox since the container is leaving the network
+	// Reinitialize the once variable so that we will be able to trigger one time
+	// sandbox initialization(again) when another container joins subsequently.
+	n.once = &sync.Once{}
+	for _, s := range n.subnets {
+		s.once = &sync.Once{}
+	}
+
+	n.destroySandbox()
+}
+
+// to be called while holding network lock
+func (n *network) destroySandbox() {
+	if n.sbox != nil {
+		for _, iface := range n.sbox.Info().Interfaces() {
+			if err := iface.Remove(); err != nil {
+				logrus.Debugf("Remove interface %s failed: %v", iface.SrcName(), err)
+			}
+		}
+
+		for _, s := range n.subnets {
+			if s.vxlanName != "" {
+				err := deleteInterface(s.vxlanName)
+				if err != nil {
+					logrus.Warnf("could not cleanup sandbox properly: %v", err)
+				}
+			}
+		}
+
+		n.sbox.Destroy()
+		n.sbox = nil
+	}
+}
+
+func networkOnceInit() {
+	if os.Getenv("_OVERLAY_HOST_MODE") != "" {
+		hostMode = true
+		return
+	}
+
+	err := createVxlan("testvxlan1", 1, 0)
+	if err != nil {
+		logrus.Errorf("Failed to create testvxlan1 interface: %v", err)
+		return
+	}
+
+	defer deleteInterface("testvxlan1")
+}
+
+func (n *network) generateVxlanName(s *subnet) string {
+	id := n.id
+	if len(n.id) > 12 {
+		id = n.id[:12]
+	}
+
+	return "vx_" + id + "_0"
+}
+
+func (n *network) generateBridgeName(s *subnet) string {
+	id := n.id
+	if len(n.id) > 5 {
+		id = n.id[:5]
+	}
+
+	return n.getBridgeNamePrefix(s) + "_" + id + "_0"
+}
+
+func (n *network) getBridgeNamePrefix(s *subnet) string {
+	return "ov_" + fmt.Sprintf("%06x", n.vxlanID(s))
+}
+
+func isOverlap(nw *net.IPNet) bool {
+	var nameservers []string
+
+	if rc, err := resolvconf.Get(); err == nil {
+		nameservers = resolvconf.GetNameserversAsCIDR(rc.Content)
+	}
+
+	if err := netutils.CheckNameserverOverlaps(nameservers, nw); err != nil {
+		return true
+	}
+
+	if err := netutils.CheckRouteOverlaps(nw); err != nil {
+		return true
+	}
+
+	return false
+}
+
+func (n *network) restoreSubnetSandbox(s *subnet, brName, vxlanName string) error {
+	sbox := n.sandbox()
+
+	// restore overlay osl sandbox
+	Ifaces := make(map[string][]osl.IfaceOption)
+	brIfaceOption := make([]osl.IfaceOption, 2)
+	brIfaceOption = append(brIfaceOption, sbox.InterfaceOptions().Address(s.gwIP))
+	brIfaceOption = append(brIfaceOption, sbox.InterfaceOptions().Bridge(true))
+	Ifaces[fmt.Sprintf("%s+%s", brName, "br")] = brIfaceOption
+
+	err := sbox.Restore(Ifaces, nil, nil, nil)
+	if err != nil {
+		return err
+	}
+
+	Ifaces = make(map[string][]osl.IfaceOption)
+	vxlanIfaceOption := make([]osl.IfaceOption, 1)
+	vxlanIfaceOption = append(vxlanIfaceOption, sbox.InterfaceOptions().Master(brName))
+	Ifaces[fmt.Sprintf("%s+%s", vxlanName, "vxlan")] = vxlanIfaceOption
+	err = sbox.Restore(Ifaces, nil, nil, nil)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (n *network) addInterface(srcName, dstPrefix, name string, isBridge bool) error {
+	return nil
+}
+
+func (n *network) setupSubnetSandbox(s *subnet, brName, vxlanName string) error {
+
+	if hostMode {
+		// Try to delete stale bridge interface if it exists
+		if err := deleteInterface(brName); err != nil {
+			deleteInterfaceBySubnet(n.getBridgeNamePrefix(s), s)
+		}
+
+		if isOverlap(s.subnetIP) {
+			return fmt.Errorf("overlay subnet %s has conflicts in the host while running in host mode", s.subnetIP.String())
+		}
+	}
+
+	if !hostMode {
+		// Try to find this subnet's vni is being used in some
+		// other namespace by looking at vniTbl that we just
+		// populated in the once init. If a hit is found then
+		// it must a stale namespace from previous
+		// life. Destroy it completely and reclaim resourced.
+		networkMu.Lock()
+		path, ok := vniTbl[n.vxlanID(s)]
+		networkMu.Unlock()
+
+		if ok {
+			os.Remove(path)
+
+			networkMu.Lock()
+			delete(vniTbl, n.vxlanID(s))
+			networkMu.Unlock()
+		}
+	}
+
+	err := createVxlan(vxlanName, n.vxlanID(s), n.maxMTU())
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (n *network) initSubnetSandbox(s *subnet, restore bool) error {
+	brName := n.generateBridgeName(s)
+	vxlanName := n.generateVxlanName(s)
+
+	if restore {
+		n.restoreSubnetSandbox(s, brName, vxlanName)
+	} else {
+		n.setupSubnetSandbox(s, brName, vxlanName)
+	}
+
+	n.Lock()
+	s.vxlanName = vxlanName
+	s.brName = brName
+	n.Unlock()
+
+	return nil
+}
+
+func (n *network) cleanupStaleSandboxes() {
+	filepath.Walk(filepath.Dir(osl.GenerateKey("walk")),
+		func(path string, info os.FileInfo, err error) error {
+			_, fname := filepath.Split(path)
+
+			pList := strings.Split(fname, "-")
+			if len(pList) <= 1 {
+				return nil
+			}
+
+			pattern := pList[1]
+			if strings.Contains(n.id, pattern) {
+				// Now that we have destroyed this
+				// sandbox, remove all references to
+				// it in vniTbl so that we don't
+				// inadvertently destroy the sandbox
+				// created in this life.
+				networkMu.Lock()
+				for vni, tblPath := range vniTbl {
+					if tblPath == path {
+						delete(vniTbl, vni)
+					}
+				}
+				networkMu.Unlock()
+			}
+
+			return nil
+		})
+}
+
+func (n *network) initSandbox(restore bool) error {
+	n.Lock()
+	n.initEpoch++
+	n.Unlock()
+
+	networkOnce.Do(networkOnceInit)
+
+	if !restore {
+		// If there are any stale sandboxes related to this network
+		// from previous daemon life clean it up here
+		n.cleanupStaleSandboxes()
+	}
+
+	// In the restore case network sandbox already exist; but we don't know
+	// what epoch number it was created with. It has to be retrieved by
+	// searching the net namespaces.
+	key := ""
+	if restore {
+		key = osl.GenerateKey("-" + n.id)
+	} else {
+		key = osl.GenerateKey(fmt.Sprintf("%d-", n.initEpoch) + n.id)
+	}
+
+	sbox, err := osl.NewSandbox(key, !hostMode, restore)
+	if err != nil {
+		return fmt.Errorf("could not get network sandbox (oper %t): %v", restore, err)
+	}
+
+	n.setSandbox(sbox)
+
+	if !restore {
+		n.driver.peerDbUpdateSandbox(n.id)
+	}
+
+	return nil
+}
+
+func (d *driver) addNetwork(n *network) {
+	d.Lock()
+	d.networks[n.id] = n
+	d.Unlock()
+}
+
+func (d *driver) deleteNetwork(nid string) {
+	d.Lock()
+	delete(d.networks, nid)
+	d.Unlock()
+}
+
+func (d *driver) network(nid string) *network {
+	d.Lock()
+	networks := d.networks
+	d.Unlock()
+
+	n, ok := networks[nid]
+	if !ok {
+		n = d.getNetworkFromStore(nid)
+		if n != nil {
+			n.driver = d
+			n.endpoints = endpointTable{}
+			n.once = &sync.Once{}
+			networks[nid] = n
+		}
+	}
+
+	return n
+}
+
+func (d *driver) getNetworkFromStore(nid string) *network {
+	if d.store == nil {
+		return nil
+	}
+
+	n := &network{id: nid}
+	if err := d.store.GetObject(datastore.Key(n.Key()...), n); err != nil {
+		return nil
+	}
+
+	return n
+}
+
+func (n *network) sandbox() osl.Sandbox {
+	n.Lock()
+	defer n.Unlock()
+
+	return n.sbox
+}
+
+func (n *network) setSandbox(sbox osl.Sandbox) {
+	n.Lock()
+	n.sbox = sbox
+	n.Unlock()
+}
+
+func (n *network) vxlanID(s *subnet) uint32 {
+	n.Lock()
+	defer n.Unlock()
+
+	return s.vni
+}
+
+func (n *network) setVxlanID(s *subnet, vni uint32) {
+	n.Lock()
+	s.vni = vni
+	n.Unlock()
+}
+
+func (n *network) Key() []string {
+	return []string{"overlay", "network", n.id}
+}
+
+func (n *network) KeyPrefix() []string {
+	return []string{"overlay", "network"}
+}
+
+func (n *network) Value() []byte {
+	m := map[string]interface{}{}
+
+	netJSON := []*subnetJSON{}
+
+	for _, s := range n.subnets {
+		sj := &subnetJSON{
+			SubnetIP: s.subnetIP.String(),
+			GwIP:     s.gwIP.String(),
+			Vni:      s.vni,
+		}
+		netJSON = append(netJSON, sj)
+	}
+
+	b, err := json.Marshal(netJSON)
+	if err != nil {
+		return []byte{}
+	}
+
+	m["secure"] = n.secure
+	m["subnets"] = netJSON
+	m["mtu"] = n.mtu
+	b, err = json.Marshal(m)
+	if err != nil {
+		return []byte{}
+	}
+
+	return b
+}
+
+func (n *network) Index() uint64 {
+	return n.dbIndex
+}
+
+func (n *network) SetIndex(index uint64) {
+	n.dbIndex = index
+	n.dbExists = true
+}
+
+func (n *network) Exists() bool {
+	return n.dbExists
+}
+
+func (n *network) Skip() bool {
+	return false
+}
+
+func (n *network) SetValue(value []byte) error {
+	var (
+		m       map[string]interface{}
+		newNet  bool
+		isMap   = true
+		netJSON = []*subnetJSON{}
+	)
+
+	if err := json.Unmarshal(value, &m); err != nil {
+		err := json.Unmarshal(value, &netJSON)
+		if err != nil {
+			return err
+		}
+		isMap = false
+	}
+
+	if len(n.subnets) == 0 {
+		newNet = true
+	}
+
+	if isMap {
+		if val, ok := m["secure"]; ok {
+			n.secure = val.(bool)
+		}
+		if val, ok := m["mtu"]; ok {
+			n.mtu = int(val.(float64))
+		}
+		bytes, err := json.Marshal(m["subnets"])
+		if err != nil {
+			return err
+		}
+		if err := json.Unmarshal(bytes, &netJSON); err != nil {
+			return err
+		}
+	}
+
+	for _, sj := range netJSON {
+		subnetIPstr := sj.SubnetIP
+		gwIPstr := sj.GwIP
+		vni := sj.Vni
+
+		subnetIP, _ := types.ParseCIDR(subnetIPstr)
+		gwIP, _ := types.ParseCIDR(gwIPstr)
+
+		if newNet {
+			s := &subnet{
+				subnetIP: subnetIP,
+				gwIP:     gwIP,
+				vni:      vni,
+				once:     &sync.Once{},
+			}
+			n.subnets = append(n.subnets, s)
+		} else {
+			sNet := n.getMatchingSubnet(subnetIP)
+			if sNet != nil {
+				sNet.vni = vni
+			}
+		}
+	}
+	return nil
+}
+
+func (n *network) DataScope() string {
+	return datastore.GlobalScope
+}
+
+func (n *network) writeToStore() error {
+	if n.driver.store == nil {
+		return nil
+	}
+
+	return n.driver.store.PutObjectAtomic(n)
+}
+
+func (n *network) releaseVxlanID() ([]uint32, error) {
+	if len(n.subnets) == 0 {
+		return nil, nil
+	}
+
+	if n.driver.store != nil {
+		if err := n.driver.store.DeleteObjectAtomic(n); err != nil {
+			if err == datastore.ErrKeyModified || err == datastore.ErrKeyNotFound {
+				// In both the above cases we can safely assume that the key has been removed by some other
+				// instance and so simply get out of here
+				return nil, nil
+			}
+
+			return nil, fmt.Errorf("failed to delete network to vxlan id map: %v", err)
+		}
+	}
+	var vnis []uint32
+	for _, s := range n.subnets {
+		if n.driver.vxlanIdm != nil {
+			vni := n.vxlanID(s)
+			vnis = append(vnis, vni)
+			n.driver.vxlanIdm.Release(uint64(vni))
+		}
+
+		n.setVxlanID(s, 0)
+	}
+
+	return vnis, nil
+}
+
+func (n *network) obtainVxlanID(s *subnet) error {
+	//return if the subnet already has a vxlan id assigned
+	if s.vni != 0 {
+		return nil
+	}
+
+	if n.driver.store == nil {
+		return fmt.Errorf("no valid vxlan id and no datastore configured, cannot obtain vxlan id")
+	}
+
+	for {
+		if err := n.driver.store.GetObject(datastore.Key(n.Key()...), n); err != nil {
+			return fmt.Errorf("getting network %q from datastore failed %v", n.id, err)
+		}
+
+		if s.vni == 0 {
+			vxlanID, err := n.driver.vxlanIdm.GetID()
+			if err != nil {
+				return fmt.Errorf("failed to allocate vxlan id: %v", err)
+			}
+
+			n.setVxlanID(s, uint32(vxlanID))
+			if err := n.writeToStore(); err != nil {
+				n.driver.vxlanIdm.Release(uint64(n.vxlanID(s)))
+				n.setVxlanID(s, 0)
+				if err == datastore.ErrKeyModified {
+					continue
+				}
+				return fmt.Errorf("network %q failed to update data store: %v", n.id, err)
+			}
+			return nil
+		}
+		return nil
+	}
+}
+
+// contains return true if the passed ip belongs to one the network's
+// subnets
+func (n *network) contains(ip net.IP) bool {
+	for _, s := range n.subnets {
+		if s.subnetIP.Contains(ip) {
+			return true
+		}
+	}
+
+	return false
+}
+
+// getSubnetforIP returns the subnet to which the given IP belongs
+func (n *network) getSubnetforIP(ip *net.IPNet) *subnet {
+	for _, s := range n.subnets {
+		// first check if the mask lengths are the same
+		i, _ := s.subnetIP.Mask.Size()
+		j, _ := ip.Mask.Size()
+		if i != j {
+			continue
+		}
+		if s.subnetIP.Contains(ip.IP) {
+			return s
+		}
+	}
+	return nil
+}
+
+// getMatchingSubnet return the network's subnet that matches the input
+func (n *network) getMatchingSubnet(ip *net.IPNet) *subnet {
+	if ip == nil {
+		return nil
+	}
+	for _, s := range n.subnets {
+		// first check if the mask lengths are the same
+		i, _ := s.subnetIP.Mask.Size()
+		j, _ := ip.Mask.Size()
+		if i != j {
+			continue
+		}
+		if s.subnetIP.IP.Equal(ip.IP) {
+			return s
+		}
+	}
+	return nil
+}

+ 233 - 0
libnetwork/drivers/solaris/overlay/ov_serf.go

@@ -0,0 +1,233 @@
+package overlay
+
+import (
+	"fmt"
+	"net"
+	"strings"
+	"time"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/hashicorp/serf/serf"
+)
+
+type ovNotify struct {
+	action string
+	ep     *endpoint
+	nw     *network
+}
+
+type logWriter struct{}
+
+func (l *logWriter) Write(p []byte) (int, error) {
+	str := string(p)
+
+	switch {
+	case strings.Contains(str, "[WARN]"):
+		logrus.Warn(str)
+	case strings.Contains(str, "[DEBUG]"):
+		logrus.Debug(str)
+	case strings.Contains(str, "[INFO]"):
+		logrus.Info(str)
+	case strings.Contains(str, "[ERR]"):
+		logrus.Error(str)
+	}
+
+	return len(p), nil
+}
+
+func (d *driver) serfInit() error {
+	var err error
+
+	config := serf.DefaultConfig()
+	config.Init()
+	config.MemberlistConfig.BindAddr = d.advertiseAddress
+
+	d.eventCh = make(chan serf.Event, 4)
+	config.EventCh = d.eventCh
+	config.UserCoalescePeriod = 1 * time.Second
+	config.UserQuiescentPeriod = 50 * time.Millisecond
+
+	config.LogOutput = &logWriter{}
+	config.MemberlistConfig.LogOutput = config.LogOutput
+
+	s, err := serf.Create(config)
+	if err != nil {
+		return fmt.Errorf("failed to create cluster node: %v", err)
+	}
+	defer func() {
+		if err != nil {
+			s.Shutdown()
+		}
+	}()
+
+	d.serfInstance = s
+
+	d.notifyCh = make(chan ovNotify)
+	d.exitCh = make(chan chan struct{})
+
+	go d.startSerfLoop(d.eventCh, d.notifyCh, d.exitCh)
+	return nil
+}
+
+func (d *driver) serfJoin(neighIP string) error {
+	if neighIP == "" {
+		return fmt.Errorf("no neighbor to join")
+	}
+	if _, err := d.serfInstance.Join([]string{neighIP}, false); err != nil {
+		return fmt.Errorf("Failed to join the cluster at neigh IP %s: %v",
+			neighIP, err)
+	}
+	return nil
+}
+
+func (d *driver) notifyEvent(event ovNotify) {
+	ep := event.ep
+
+	ePayload := fmt.Sprintf("%s %s %s %s", event.action, ep.addr.IP.String(),
+		net.IP(ep.addr.Mask).String(), ep.mac.String())
+	eName := fmt.Sprintf("jl %s %s %s", d.serfInstance.LocalMember().Addr.String(),
+		event.nw.id, ep.id)
+
+	if err := d.serfInstance.UserEvent(eName, []byte(ePayload), true); err != nil {
+		logrus.Errorf("Sending user event failed: %v\n", err)
+	}
+}
+
+func (d *driver) processEvent(u serf.UserEvent) {
+	logrus.Debugf("Received user event name:%s, payload:%s\n", u.Name,
+		string(u.Payload))
+
+	var dummy, action, vtepStr, nid, eid, ipStr, maskStr, macStr string
+	if _, err := fmt.Sscan(u.Name, &dummy, &vtepStr, &nid, &eid); err != nil {
+		fmt.Printf("Failed to scan name string: %v\n", err)
+	}
+
+	if _, err := fmt.Sscan(string(u.Payload), &action,
+		&ipStr, &maskStr, &macStr); err != nil {
+		fmt.Printf("Failed to scan value string: %v\n", err)
+	}
+
+	logrus.Debugf("Parsed data = %s/%s/%s/%s/%s/%s\n", nid, eid, vtepStr, ipStr, maskStr, macStr)
+
+	mac, err := net.ParseMAC(macStr)
+	if err != nil {
+		logrus.Errorf("Failed to parse mac: %v\n", err)
+	}
+
+	if d.serfInstance.LocalMember().Addr.String() == vtepStr {
+		return
+	}
+
+	switch action {
+	case "join":
+		if err := d.peerAdd(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac,
+			net.ParseIP(vtepStr), true); err != nil {
+			logrus.Errorf("Peer add failed in the driver: %v\n", err)
+		}
+	case "leave":
+		if err := d.peerDelete(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac,
+			net.ParseIP(vtepStr), true); err != nil {
+			logrus.Errorf("Peer delete failed in the driver: %v\n", err)
+		}
+	}
+}
+
+func (d *driver) processQuery(q *serf.Query) {
+	logrus.Debugf("Received query name:%s, payload:%s\n", q.Name,
+		string(q.Payload))
+
+	var nid, ipStr string
+	if _, err := fmt.Sscan(string(q.Payload), &nid, &ipStr); err != nil {
+		fmt.Printf("Failed to scan query payload string: %v\n", err)
+	}
+
+	peerMac, peerIPMask, vtep, err := d.peerDbSearch(nid, net.ParseIP(ipStr))
+	if err != nil {
+		return
+	}
+
+	q.Respond([]byte(fmt.Sprintf("%s %s %s", peerMac.String(), net.IP(peerIPMask).String(), vtep.String())))
+}
+
+func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
+	if d.serfInstance == nil {
+		return nil, nil, nil, fmt.Errorf("could not resolve peer: serf instance not initialized")
+	}
+
+	qPayload := fmt.Sprintf("%s %s", string(nid), peerIP.String())
+	resp, err := d.serfInstance.Query("peerlookup", []byte(qPayload), nil)
+	if err != nil {
+		return nil, nil, nil, fmt.Errorf("resolving peer by querying the cluster failed: %v", err)
+	}
+
+	respCh := resp.ResponseCh()
+	select {
+	case r := <-respCh:
+		var macStr, maskStr, vtepStr string
+		if _, err := fmt.Sscan(string(r.Payload), &macStr, &maskStr, &vtepStr); err != nil {
+			return nil, nil, nil, fmt.Errorf("bad response %q for the resolve query: %v", string(r.Payload), err)
+		}
+
+		mac, err := net.ParseMAC(macStr)
+		if err != nil {
+			return nil, nil, nil, fmt.Errorf("failed to parse mac: %v", err)
+		}
+
+		return mac, net.IPMask(net.ParseIP(maskStr).To4()), net.ParseIP(vtepStr), nil
+
+	case <-time.After(time.Second):
+		return nil, nil, nil, fmt.Errorf("timed out resolving peer by querying the cluster")
+	}
+}
+
+func (d *driver) startSerfLoop(eventCh chan serf.Event, notifyCh chan ovNotify,
+	exitCh chan chan struct{}) {
+
+	for {
+		select {
+		case notify, ok := <-notifyCh:
+			if !ok {
+				break
+			}
+
+			d.notifyEvent(notify)
+		case ch, ok := <-exitCh:
+			if !ok {
+				break
+			}
+
+			if err := d.serfInstance.Leave(); err != nil {
+				logrus.Errorf("failed leaving the cluster: %v\n", err)
+			}
+
+			d.serfInstance.Shutdown()
+			close(ch)
+			return
+		case e, ok := <-eventCh:
+			if !ok {
+				break
+			}
+
+			if e.EventType() == serf.EventQuery {
+				d.processQuery(e.(*serf.Query))
+				break
+			}
+
+			u, ok := e.(serf.UserEvent)
+			if !ok {
+				break
+			}
+			d.processEvent(u)
+		}
+	}
+}
+
+func (d *driver) isSerfAlive() bool {
+	d.Lock()
+	serfInstance := d.serfInstance
+	d.Unlock()
+	if serfInstance == nil || serfInstance.State() != serf.SerfAlive {
+		return false
+	}
+	return true
+}

+ 61 - 0
libnetwork/drivers/solaris/overlay/ov_utils.go

@@ -0,0 +1,61 @@
+package overlay
+
+import (
+	"fmt"
+	"os/exec"
+	"strings"
+
+	"github.com/docker/libnetwork/osl"
+)
+
+func validateID(nid, eid string) error {
+	if nid == "" {
+		return fmt.Errorf("invalid network id")
+	}
+
+	if eid == "" {
+		return fmt.Errorf("invalid endpoint id")
+	}
+
+	return nil
+}
+
+func createVxlan(name string, vni uint32, mtu int) error {
+	defer osl.InitOSContext()()
+
+	// Get default interface to plumb the vxlan on
+	routeCmd := "/usr/sbin/ipadm show-addr -p -o addrobj " +
+		"`/usr/sbin/route get default | /usr/bin/grep interface | " +
+		"/usr/bin/awk '{print $2}'`"
+	out, err := exec.Command("/usr/bin/bash", "-c", routeCmd).Output()
+	if err != nil {
+		return fmt.Errorf("cannot get default route: %v", err)
+	}
+
+	defaultInterface := strings.SplitN(string(out), "/", 2)
+	propList := fmt.Sprintf("interface=%s,vni=%d", defaultInterface[0], vni)
+
+	out, err = exec.Command("/usr/sbin/dladm", "create-vxlan", "-t", "-p", propList,
+		name).Output()
+	if err != nil {
+		return fmt.Errorf("error creating vxlan interface: %v %s", err, out)
+	}
+
+	return nil
+}
+
+func deleteInterfaceBySubnet(brPrefix string, s *subnet) error {
+	return nil
+
+}
+
+func deleteInterface(name string) error {
+	defer osl.InitOSContext()()
+
+	out, err := exec.Command("/usr/sbin/dladm", "delete-vxlan", name).Output()
+	if err != nil {
+		return fmt.Errorf("error creating vxlan interface: %v %s", err, out)
+	}
+
+	return nil
+}

+ 362 - 0
libnetwork/drivers/solaris/overlay/overlay.go

@@ -0,0 +1,362 @@
+package overlay
+
+//go:generate protoc -I.:../../Godeps/_workspace/src/github.com/gogo/protobuf  --gogo_out=import_path=github.com/docker/libnetwork/drivers/overlay,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. overlay.proto
+
+import (
+	"fmt"
+	"net"
+	"sync"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/datastore"
+	"github.com/docker/libnetwork/discoverapi"
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/idm"
+	"github.com/docker/libnetwork/netlabel"
+	"github.com/docker/libnetwork/osl"
+	"github.com/docker/libnetwork/types"
+	"github.com/hashicorp/serf/serf"
+)
+
+// XXX OVERLAY_SOLARIS
+// Might need changes for names/constant values in solaris
+const (
+	networkType  = "overlay"
+	vethPrefix   = "veth"
+	vethLen      = 7
+	vxlanIDStart = 256
+	vxlanIDEnd   = (1 << 24) - 1
+	vxlanPort    = 4789
+	vxlanEncap   = 50
+	secureOption = "encrypted"
+)
+
+var initVxlanIdm = make(chan (bool), 1)
+
+type driver struct {
+	eventCh          chan serf.Event
+	notifyCh         chan ovNotify
+	exitCh           chan chan struct{}
+	bindAddress      string
+	advertiseAddress string
+	neighIP          string
+	config           map[string]interface{}
+	peerDb           peerNetworkMap
+	secMap           *encrMap
+	serfInstance     *serf.Serf
+	networks         networkTable
+	store            datastore.DataStore
+	localStore       datastore.DataStore
+	vxlanIdm         *idm.Idm
+	once             sync.Once
+	joinOnce         sync.Once
+	keys             []*key
+	sync.Mutex
+}
+
+// Init registers a new instance of overlay driver
+func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+	c := driverapi.Capability{
+		DataScope: datastore.GlobalScope,
+	}
+	d := &driver{
+		networks: networkTable{},
+		peerDb: peerNetworkMap{
+			mp: map[string]*peerMap{},
+		},
+		secMap: &encrMap{nodes: map[string][]*spi{}},
+		config: config,
+	}
+
+	if data, ok := config[netlabel.GlobalKVClient]; ok {
+		var err error
+		dsc, ok := data.(discoverapi.DatastoreConfigData)
+		if !ok {
+			return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
+		}
+		d.store, err = datastore.NewDataStoreFromConfig(dsc)
+		if err != nil {
+			return types.InternalErrorf("failed to initialize data store: %v", err)
+		}
+	}
+
+	if data, ok := config[netlabel.LocalKVClient]; ok {
+		var err error
+		dsc, ok := data.(discoverapi.DatastoreConfigData)
+		if !ok {
+			return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
+		}
+		d.localStore, err = datastore.NewDataStoreFromConfig(dsc)
+		if err != nil {
+			return types.InternalErrorf("failed to initialize local data store: %v", err)
+		}
+	}
+
+	d.restoreEndpoints()
+
+	return dc.RegisterDriver(networkType, d, c)
+}
+
+// Endpoints are stored in the local store. Restore them and reconstruct the overlay sandbox
+func (d *driver) restoreEndpoints() error {
+	if d.localStore == nil {
+		logrus.Warnf("Cannot restore overlay endpoints because local datastore is missing")
+		return nil
+	}
+	kvol, err := d.localStore.List(datastore.Key(overlayEndpointPrefix), &endpoint{})
+	if err != nil && err != datastore.ErrKeyNotFound {
+		return fmt.Errorf("failed to read overlay endpoint from store: %v", err)
+	}
+
+	if err == datastore.ErrKeyNotFound {
+		return nil
+	}
+	for _, kvo := range kvol {
+		ep := kvo.(*endpoint)
+		n := d.network(ep.nid)
+		if n == nil {
+			logrus.Debugf("Network (%s) not found for restored endpoint (%s)", ep.nid[0:7], ep.id[0:7])
+			logrus.Debugf("Deleting stale overlay endpoint (%s) from store", ep.id[0:7])
+			if err := d.deleteEndpointFromStore(ep); err != nil {
+				logrus.Debugf("Failed to delete stale overlay endpoint (%s) from store", ep.id[0:7])
+			}
+			continue
+		}
+		n.addEndpoint(ep)
+
+		s := n.getSubnetforIP(ep.addr)
+		if s == nil {
+			return fmt.Errorf("could not find subnet for endpoint %s", ep.id)
+		}
+
+		if err := n.joinSandbox(true); err != nil {
+			return fmt.Errorf("restore network sandbox failed: %v", err)
+		}
+
+		if err := n.joinSubnetSandbox(s, true); err != nil {
+			return fmt.Errorf("restore subnet sandbox failed for %q: %v", s.subnetIP.String(), err)
+		}
+
+		Ifaces := make(map[string][]osl.IfaceOption)
+		vethIfaceOption := make([]osl.IfaceOption, 1)
+		vethIfaceOption = append(vethIfaceOption, n.sbox.InterfaceOptions().Master(s.brName))
+		Ifaces[fmt.Sprintf("%s+%s", "veth", "veth")] = vethIfaceOption
+
+		err := n.sbox.Restore(Ifaces, nil, nil, nil)
+		if err != nil {
+			return fmt.Errorf("failed to restore overlay sandbox: %v", err)
+		}
+
+		n.incEndpointCount()
+		d.peerDbAdd(ep.nid, ep.id, ep.addr.IP, ep.addr.Mask, ep.mac, net.ParseIP(d.advertiseAddress), true)
+	}
+	return nil
+}
+
+// Fini cleans up the driver resources
+func Fini(drv driverapi.Driver) {
+	d := drv.(*driver)
+
+	if d.exitCh != nil {
+		waitCh := make(chan struct{})
+
+		d.exitCh <- waitCh
+
+		<-waitCh
+	}
+}
+
+func (d *driver) configure() error {
+	if d.store == nil {
+		return nil
+	}
+
+	if d.vxlanIdm == nil {
+		return d.initializeVxlanIdm()
+	}
+
+	return nil
+}
+
+func (d *driver) initializeVxlanIdm() error {
+	var err error
+
+	initVxlanIdm <- true
+	defer func() { <-initVxlanIdm }()
+
+	if d.vxlanIdm != nil {
+		return nil
+	}
+
+	d.vxlanIdm, err = idm.New(d.store, "vxlan-id", vxlanIDStart, vxlanIDEnd)
+	if err != nil {
+		return fmt.Errorf("failed to initialize vxlan id manager: %v", err)
+	}
+
+	return nil
+}
+
+func (d *driver) Type() string {
+	return networkType
+}
+
+func validateSelf(node string) error {
+	advIP := net.ParseIP(node)
+	if advIP == nil {
+		return fmt.Errorf("invalid self address (%s)", node)
+	}
+
+	addrs, err := net.InterfaceAddrs()
+	if err != nil {
+		return fmt.Errorf("Unable to get interface addresses %v", err)
+	}
+	for _, addr := range addrs {
+		ip, _, err := net.ParseCIDR(addr.String())
+		if err == nil && ip.Equal(advIP) {
+			return nil
+		}
+	}
+	return fmt.Errorf("Multi-Host overlay networking requires cluster-advertise(%s) to be configured with a local ip-address that is reachable within the cluster", advIP.String())
+}
+
+func (d *driver) nodeJoin(advertiseAddress, bindAddress string, self bool) {
+	if self && !d.isSerfAlive() {
+		d.Lock()
+		d.advertiseAddress = advertiseAddress
+		d.bindAddress = bindAddress
+		d.Unlock()
+
+		// If there is no cluster store there is no need to start serf.
+		if d.store != nil {
+			if err := validateSelf(advertiseAddress); err != nil {
+				logrus.Warnf("%s", err.Error())
+			}
+			err := d.serfInit()
+			if err != nil {
+				logrus.Errorf("initializing serf instance failed: %v", err)
+				d.Lock()
+				d.advertiseAddress = ""
+				d.bindAddress = ""
+				d.Unlock()
+				return
+			}
+		}
+	}
+
+	d.Lock()
+	if !self {
+		d.neighIP = advertiseAddress
+	}
+	neighIP := d.neighIP
+	d.Unlock()
+
+	if d.serfInstance != nil && neighIP != "" {
+		var err error
+		d.joinOnce.Do(func() {
+			err = d.serfJoin(neighIP)
+			if err == nil {
+				d.pushLocalDb()
+			}
+		})
+		if err != nil {
+			logrus.Errorf("joining serf neighbor %s failed: %v", advertiseAddress, err)
+			d.Lock()
+			d.joinOnce = sync.Once{}
+			d.Unlock()
+			return
+		}
+	}
+}
+
+func (d *driver) pushLocalEndpointEvent(action, nid, eid string) {
+	n := d.network(nid)
+	if n == nil {
+		logrus.Debugf("Error pushing local endpoint event for network %s", nid)
+		return
+	}
+	ep := n.endpoint(eid)
+	if ep == nil {
+		logrus.Debugf("Error pushing local endpoint event for ep %s / %s", nid, eid)
+		return
+	}
+
+	if !d.isSerfAlive() {
+		return
+	}
+	d.notifyCh <- ovNotify{
+		action: "join",
+		nw:     n,
+		ep:     ep,
+	}
+}
+
+// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
+func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
+	var err error
+	switch dType {
+	case discoverapi.NodeDiscovery:
+		nodeData, ok := data.(discoverapi.NodeDiscoveryData)
+		if !ok || nodeData.Address == "" {
+			return fmt.Errorf("invalid discovery data")
+		}
+		d.nodeJoin(nodeData.Address, nodeData.BindAddress, nodeData.Self)
+	case discoverapi.DatastoreConfig:
+		if d.store != nil {
+			return types.ForbiddenErrorf("cannot accept datastore configuration: Overlay driver has a datastore configured already")
+		}
+		dsc, ok := data.(discoverapi.DatastoreConfigData)
+		if !ok {
+			return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
+		}
+		d.store, err = datastore.NewDataStoreFromConfig(dsc)
+		if err != nil {
+			return types.InternalErrorf("failed to initialize data store: %v", err)
+		}
+	case discoverapi.EncryptionKeysConfig:
+		encrData, ok := data.(discoverapi.DriverEncryptionConfig)
+		if !ok {
+			return fmt.Errorf("invalid encryption key notification data")
+		}
+		keys := make([]*key, 0, len(encrData.Keys))
+		for i := 0; i < len(encrData.Keys); i++ {
+			k := &key{
+				value: encrData.Keys[i],
+				tag:   uint32(encrData.Tags[i]),
+			}
+			keys = append(keys, k)
+		}
+		d.setKeys(keys)
+	case discoverapi.EncryptionKeysUpdate:
+		var newKey, delKey, priKey *key
+		encrData, ok := data.(discoverapi.DriverEncryptionUpdate)
+		if !ok {
+			return fmt.Errorf("invalid encryption key notification data")
+		}
+		if encrData.Key != nil {
+			newKey = &key{
+				value: encrData.Key,
+				tag:   uint32(encrData.Tag),
+			}
+		}
+		if encrData.Primary != nil {
+			priKey = &key{
+				value: encrData.Primary,
+				tag:   uint32(encrData.PrimaryTag),
+			}
+		}
+		if encrData.Prune != nil {
+			delKey = &key{
+				value: encrData.Prune,
+				tag:   uint32(encrData.PruneTag),
+			}
+		}
+		d.updateKeys(newKey, priKey, delKey)
+	default:
+	}
+	return nil
+}
+
+// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster
+func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
+	return nil
+}

+ 468 - 0
libnetwork/drivers/solaris/overlay/overlay.pb.go

@@ -0,0 +1,468 @@
+// Code generated by protoc-gen-gogo.
+// source: overlay.proto
+// DO NOT EDIT!
+
+/*
+	Package overlay is a generated protocol buffer package.
+
+	It is generated from these files:
+		overlay.proto
+
+	It has these top-level messages:
+		PeerRecord
+*/
+package overlay
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import _ "github.com/gogo/protobuf/gogoproto"
+
+import strings "strings"
+import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
+import sort "sort"
+import strconv "strconv"
+import reflect "reflect"
+
+import io "io"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.GoGoProtoPackageIsVersion1
+
+// PeerRecord defines the information corresponding to a peer
+// container in the overlay network.
+type PeerRecord struct {
+	// Endpoint IP is the IP of the container attachment on the
+	// given overlay network.
+	EndpointIP string `protobuf:"bytes,1,opt,name=endpoint_ip,json=endpointIp,proto3" json:"endpoint_ip,omitempty"`
+	// Endpoint MAC is the mac address of the container attachment
+	// on the given overlay network.
+	EndpointMAC string `protobuf:"bytes,2,opt,name=endpoint_mac,json=endpointMac,proto3" json:"endpoint_mac,omitempty"`
+	// Tunnel Endpoint IP defines the host IP for the host in
+	// which this container is running and can be reached by
+	// building a tunnel to that host IP.
+	TunnelEndpointIP string `protobuf:"bytes,3,opt,name=tunnel_endpoint_ip,json=tunnelEndpointIp,proto3" json:"tunnel_endpoint_ip,omitempty"`
+}
+
+func (m *PeerRecord) Reset()                    { *m = PeerRecord{} }
+func (*PeerRecord) ProtoMessage()               {}
+func (*PeerRecord) Descriptor() ([]byte, []int) { return fileDescriptorOverlay, []int{0} }
+
+func init() {
+	proto.RegisterType((*PeerRecord)(nil), "overlay.PeerRecord")
+}
+func (this *PeerRecord) GoString() string {
+	if this == nil {
+		return "nil"
+	}
+	s := make([]string, 0, 7)
+	s = append(s, "&overlay.PeerRecord{")
+	s = append(s, "EndpointIP: "+fmt.Sprintf("%#v", this.EndpointIP)+",\n")
+	s = append(s, "EndpointMAC: "+fmt.Sprintf("%#v", this.EndpointMAC)+",\n")
+	s = append(s, "TunnelEndpointIP: "+fmt.Sprintf("%#v", this.TunnelEndpointIP)+",\n")
+	s = append(s, "}")
+	return strings.Join(s, "")
+}
+func valueToGoStringOverlay(v interface{}, typ string) string {
+	rv := reflect.ValueOf(v)
+	if rv.IsNil() {
+		return "nil"
+	}
+	pv := reflect.Indirect(rv).Interface()
+	return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv)
+}
+func extensionToGoStringOverlay(e map[int32]github_com_gogo_protobuf_proto.Extension) string {
+	if e == nil {
+		return "nil"
+	}
+	s := "map[int32]proto.Extension{"
+	keys := make([]int, 0, len(e))
+	for k := range e {
+		keys = append(keys, int(k))
+	}
+	sort.Ints(keys)
+	ss := []string{}
+	for _, k := range keys {
+		ss = append(ss, strconv.Itoa(k)+": "+e[int32(k)].GoString())
+	}
+	s += strings.Join(ss, ",") + "}"
+	return s
+}
+func (m *PeerRecord) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *PeerRecord) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.EndpointIP) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintOverlay(data, i, uint64(len(m.EndpointIP)))
+		i += copy(data[i:], m.EndpointIP)
+	}
+	if len(m.EndpointMAC) > 0 {
+		data[i] = 0x12
+		i++
+		i = encodeVarintOverlay(data, i, uint64(len(m.EndpointMAC)))
+		i += copy(data[i:], m.EndpointMAC)
+	}
+	if len(m.TunnelEndpointIP) > 0 {
+		data[i] = 0x1a
+		i++
+		i = encodeVarintOverlay(data, i, uint64(len(m.TunnelEndpointIP)))
+		i += copy(data[i:], m.TunnelEndpointIP)
+	}
+	return i, nil
+}
+
+func encodeFixed64Overlay(data []byte, offset int, v uint64) int {
+	data[offset] = uint8(v)
+	data[offset+1] = uint8(v >> 8)
+	data[offset+2] = uint8(v >> 16)
+	data[offset+3] = uint8(v >> 24)
+	data[offset+4] = uint8(v >> 32)
+	data[offset+5] = uint8(v >> 40)
+	data[offset+6] = uint8(v >> 48)
+	data[offset+7] = uint8(v >> 56)
+	return offset + 8
+}
+func encodeFixed32Overlay(data []byte, offset int, v uint32) int {
+	data[offset] = uint8(v)
+	data[offset+1] = uint8(v >> 8)
+	data[offset+2] = uint8(v >> 16)
+	data[offset+3] = uint8(v >> 24)
+	return offset + 4
+}
+func encodeVarintOverlay(data []byte, offset int, v uint64) int {
+	for v >= 1<<7 {
+		data[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	data[offset] = uint8(v)
+	return offset + 1
+}
+func (m *PeerRecord) Size() (n int) {
+	var l int
+	_ = l
+	l = len(m.EndpointIP)
+	if l > 0 {
+		n += 1 + l + sovOverlay(uint64(l))
+	}
+	l = len(m.EndpointMAC)
+	if l > 0 {
+		n += 1 + l + sovOverlay(uint64(l))
+	}
+	l = len(m.TunnelEndpointIP)
+	if l > 0 {
+		n += 1 + l + sovOverlay(uint64(l))
+	}
+	return n
+}
+
+func sovOverlay(x uint64) (n int) {
+	for {
+		n++
+		x >>= 7
+		if x == 0 {
+			break
+		}
+	}
+	return n
+}
+func sozOverlay(x uint64) (n int) {
+	return sovOverlay(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (this *PeerRecord) String() string {
+	if this == nil {
+		return "nil"
+	}
+	s := strings.Join([]string{`&PeerRecord{`,
+		`EndpointIP:` + fmt.Sprintf("%v", this.EndpointIP) + `,`,
+		`EndpointMAC:` + fmt.Sprintf("%v", this.EndpointMAC) + `,`,
+		`TunnelEndpointIP:` + fmt.Sprintf("%v", this.TunnelEndpointIP) + `,`,
+		`}`,
+	}, "")
+	return s
+}
+func valueToStringOverlay(v interface{}) string {
+	rv := reflect.ValueOf(v)
+	if rv.IsNil() {
+		return "nil"
+	}
+	pv := reflect.Indirect(rv).Interface()
+	return fmt.Sprintf("*%v", pv)
+}
+func (m *PeerRecord) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowOverlay
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: PeerRecord: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: PeerRecord: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field EndpointIP", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowOverlay
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthOverlay
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.EndpointIP = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field EndpointMAC", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowOverlay
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthOverlay
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.EndpointMAC = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TunnelEndpointIP", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowOverlay
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthOverlay
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.TunnelEndpointIP = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipOverlay(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthOverlay
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipOverlay(data []byte) (n int, err error) {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowOverlay
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowOverlay
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if data[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+			return iNdEx, nil
+		case 1:
+			iNdEx += 8
+			return iNdEx, nil
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowOverlay
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			iNdEx += length
+			if length < 0 {
+				return 0, ErrInvalidLengthOverlay
+			}
+			return iNdEx, nil
+		case 3:
+			for {
+				var innerWire uint64
+				var start int = iNdEx
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return 0, ErrIntOverflowOverlay
+					}
+					if iNdEx >= l {
+						return 0, io.ErrUnexpectedEOF
+					}
+					b := data[iNdEx]
+					iNdEx++
+					innerWire |= (uint64(b) & 0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				innerWireType := int(innerWire & 0x7)
+				if innerWireType == 4 {
+					break
+				}
+				next, err := skipOverlay(data[start:])
+				if err != nil {
+					return 0, err
+				}
+				iNdEx = start + next
+			}
+			return iNdEx, nil
+		case 4:
+			return iNdEx, nil
+		case 5:
+			iNdEx += 4
+			return iNdEx, nil
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+	}
+	panic("unreachable")
+}
+
+var (
+	ErrInvalidLengthOverlay = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowOverlay   = fmt.Errorf("proto: integer overflow")
+)
+
+var fileDescriptorOverlay = []byte{
+	// 195 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x2f, 0x4b, 0x2d,
+	0xca, 0x49, 0xac, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x72, 0xa5, 0x44, 0xd2,
+	0xf3, 0xd3, 0xf3, 0xc1, 0x62, 0xfa, 0x20, 0x16, 0x44, 0x5a, 0x69, 0x2b, 0x23, 0x17, 0x57, 0x40,
+	0x6a, 0x6a, 0x51, 0x50, 0x6a, 0x72, 0x7e, 0x51, 0x8a, 0x90, 0x3e, 0x17, 0x77, 0x6a, 0x5e, 0x4a,
+	0x41, 0x7e, 0x66, 0x5e, 0x49, 0x7c, 0x66, 0x81, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xa7, 0x13, 0xdf,
+	0xa3, 0x7b, 0xf2, 0x5c, 0xae, 0x50, 0x61, 0xcf, 0x80, 0x20, 0x2e, 0x98, 0x12, 0xcf, 0x02, 0x21,
+	0x23, 0x2e, 0x1e, 0xb8, 0x86, 0xdc, 0xc4, 0x64, 0x09, 0x26, 0xb0, 0x0e, 0x7e, 0xa0, 0x0e, 0x6e,
+	0x98, 0x0e, 0x5f, 0x47, 0xe7, 0x20, 0xb8, 0xa9, 0xbe, 0x89, 0xc9, 0x42, 0x4e, 0x5c, 0x42, 0x25,
+	0xa5, 0x79, 0x79, 0xa9, 0x39, 0xf1, 0xc8, 0x76, 0x31, 0x83, 0x75, 0x8a, 0x00, 0x75, 0x0a, 0x84,
+	0x80, 0x65, 0x91, 0x6c, 0x14, 0x28, 0x41, 0x15, 0x29, 0x70, 0x92, 0xb8, 0xf1, 0x50, 0x8e, 0xe1,
+	0xc3, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x27, 0x80, 0xf8, 0x02, 0x10, 0x3f, 0x00, 0xe2,
+	0x24, 0x36, 0xb0, 0xc7, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbf, 0xd7, 0x7d, 0x7d, 0x08,
+	0x01, 0x00, 0x00,
+}

+ 27 - 0
libnetwork/drivers/solaris/overlay/overlay.proto

@@ -0,0 +1,27 @@
+syntax = "proto3";
+
+import "gogoproto/gogo.proto";
+
+package overlay;
+
+option (gogoproto.marshaler_all) = true;
+option (gogoproto.unmarshaler_all) = true;
+option (gogoproto.stringer_all) = true;
+option (gogoproto.gostring_all) = true;
+option (gogoproto.sizer_all) = true;
+option (gogoproto.goproto_stringer_all) = false;
+
+// PeerRecord defines the information corresponding to a peer
+// container in the overlay network.
+message PeerRecord {
+	// Endpoint IP is the IP of the container attachment on the
+	// given overlay network.
+	string endpoint_ip = 1 [(gogoproto.customname) = "EndpointIP"];
+	// Endpoint MAC is the mac address of the container attachment
+	// on the given overlay network.
+	string endpoint_mac = 2 [(gogoproto.customname) = "EndpointMAC"];
+	// Tunnel Endpoint IP defines the host IP for the host in
+	// which this container is running and can be reached by
+	// building a tunnel to that host IP.
+	string tunnel_endpoint_ip = 3 [(gogoproto.customname) = "TunnelEndpointIP"];
+}

+ 138 - 0
libnetwork/drivers/solaris/overlay/overlay_test.go

@@ -0,0 +1,138 @@
+// +build solaris
+
+package overlay
+
+import (
+	"os/exec"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/pkg/plugingetter"
+	"github.com/docker/libkv/store/consul"
+	"github.com/docker/libnetwork/datastore"
+	"github.com/docker/libnetwork/discoverapi"
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/netlabel"
+	_ "github.com/docker/libnetwork/testutils"
+)
+
+func init() {
+	consul.Register()
+}
+
+type driverTester struct {
+	t *testing.T
+	d *driver
+}
+
+const testNetworkType = "overlay"
+
+func setupDriver(t *testing.T) *driverTester {
+	dt := &driverTester{t: t}
+	config := make(map[string]interface{})
+	config[netlabel.GlobalKVClient] = discoverapi.DatastoreConfigData{
+		Scope:    datastore.GlobalScope,
+		Provider: "consul",
+		Address:  "127.0.0.01:8500",
+	}
+
+	if err := Init(dt, config); err != nil {
+		t.Fatal(err)
+	}
+
+	// Use net0 as default interface
+	ifcfgCmd := "/usr/sbin/ifconfig net0 | grep inet | awk '{print $2}'"
+	out, err := exec.Command("/usr/bin/bash", "-c", ifcfgCmd).Output()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	data := discoverapi.NodeDiscoveryData{
+		Address: string(out),
+		Self:    true,
+	}
+	dt.d.DiscoverNew(discoverapi.NodeDiscovery, data)
+	return dt
+}
+
+func cleanupDriver(t *testing.T, dt *driverTester) {
+	ch := make(chan struct{})
+	go func() {
+		Fini(dt.d)
+		close(ch)
+	}()
+
+	select {
+	case <-ch:
+	case <-time.After(10 * time.Second):
+		t.Fatal("test timed out because Fini() did not return on time")
+	}
+}
+
+func (dt *driverTester) GetPluginGetter() plugingetter.PluginGetter {
+	return nil
+}
+
+func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
+	cap driverapi.Capability) error {
+	if name != testNetworkType {
+		dt.t.Fatalf("Expected driver register name to be %q. Instead got %q",
+			testNetworkType, name)
+	}
+
+	if _, ok := drv.(*driver); !ok {
+		dt.t.Fatalf("Expected driver type to be %T. Instead got %T",
+			&driver{}, drv)
+	}
+
+	dt.d = drv.(*driver)
+	return nil
+}
+
+func TestOverlayInit(t *testing.T) {
+	if err := Init(&driverTester{t: t}, nil); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestOverlayFiniWithoutConfig(t *testing.T) {
+	dt := &driverTester{t: t}
+	if err := Init(dt, nil); err != nil {
+		t.Fatal(err)
+	}
+
+	cleanupDriver(t, dt)
+}
+
+func TestOverlayConfig(t *testing.T) {
+	dt := setupDriver(t)
+
+	time.Sleep(1 * time.Second)
+
+	d := dt.d
+	if d.notifyCh == nil {
+		t.Fatal("Driver notify channel wasn't initialzed after Config method")
+	}
+
+	if d.exitCh == nil {
+		t.Fatal("Driver serfloop exit channel wasn't initialzed after Config method")
+	}
+
+	if d.serfInstance == nil {
+		t.Fatal("Driver serfinstance  hasn't been initialized after Config method")
+	}
+
+	cleanupDriver(t, dt)
+}
+
+func TestOverlayType(t *testing.T) {
+	dt := &driverTester{t: t}
+	if err := Init(dt, nil); err != nil {
+		t.Fatal(err)
+	}
+
+	if dt.d.Type() != testNetworkType {
+		t.Fatalf("Expected Type() to return %q. Instead got %q", testNetworkType,
+			dt.d.Type())
+	}
+}

+ 248 - 0
libnetwork/drivers/solaris/overlay/ovmanager/ovmanager.go

@@ -0,0 +1,248 @@
+package ovmanager
+
+import (
+	"fmt"
+	"net"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/datastore"
+	"github.com/docker/libnetwork/discoverapi"
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/idm"
+	"github.com/docker/libnetwork/netlabel"
+	"github.com/docker/libnetwork/types"
+)
+
+const (
+	networkType  = "overlay"
+	vxlanIDStart = 256
+	vxlanIDEnd   = (1 << 24) - 1
+)
+
+type networkTable map[string]*network
+
+type driver struct {
+	config   map[string]interface{}
+	networks networkTable
+	store    datastore.DataStore
+	vxlanIdm *idm.Idm
+	sync.Mutex
+}
+
+type subnet struct {
+	subnetIP *net.IPNet
+	gwIP     *net.IPNet
+	vni      uint32
+}
+
+type network struct {
+	id      string
+	driver  *driver
+	subnets []*subnet
+	sync.Mutex
+}
+
+// Init registers a new instance of overlay driver
+func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
+	var err error
+	c := driverapi.Capability{
+		DataScope: datastore.GlobalScope,
+	}
+
+	d := &driver{
+		networks: networkTable{},
+		config:   config,
+	}
+
+	d.vxlanIdm, err = idm.New(nil, "vxlan-id", vxlanIDStart, vxlanIDEnd)
+	if err != nil {
+		return fmt.Errorf("failed to initialize vxlan id manager: %v", err)
+	}
+
+	return dc.RegisterDriver(networkType, d, c)
+}
+
+func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
+	if id == "" {
+		return nil, fmt.Errorf("invalid network id for overlay network")
+	}
+
+	if ipV4Data == nil {
+		return nil, fmt.Errorf("empty ipv4 data passed during overlay network creation")
+	}
+
+	n := &network{
+		id:      id,
+		driver:  d,
+		subnets: []*subnet{},
+	}
+
+	opts := make(map[string]string)
+	vxlanIDList := make([]uint32, 0, len(ipV4Data))
+	for key, val := range option {
+		if key == netlabel.OverlayVxlanIDList {
+			logrus.Debugf("overlay network option: %s", val)
+			valStrList := strings.Split(val, ",")
+			for _, idStr := range valStrList {
+				vni, err := strconv.Atoi(idStr)
+				if err != nil {
+					return nil, fmt.Errorf("invalid vxlan id value %q passed", idStr)
+				}
+
+				vxlanIDList = append(vxlanIDList, uint32(vni))
+			}
+		} else {
+			opts[key] = val
+		}
+	}
+
+	for i, ipd := range ipV4Data {
+		s := &subnet{
+			subnetIP: ipd.Pool,
+			gwIP:     ipd.Gateway,
+		}
+
+		if len(vxlanIDList) > i {
+			s.vni = vxlanIDList[i]
+		}
+
+		if err := n.obtainVxlanID(s); err != nil {
+			n.releaseVxlanID()
+			return nil, fmt.Errorf("could not obtain vxlan id for pool %s: %v", s.subnetIP, err)
+		}
+
+		n.subnets = append(n.subnets, s)
+	}
+
+	val := fmt.Sprintf("%d", n.subnets[0].vni)
+	for _, s := range n.subnets[1:] {
+		val = val + fmt.Sprintf(",%d", s.vni)
+	}
+	opts[netlabel.OverlayVxlanIDList] = val
+
+	d.Lock()
+	d.networks[id] = n
+	d.Unlock()
+
+	return opts, nil
+}
+
+func (d *driver) NetworkFree(id string) error {
+	if id == "" {
+		return fmt.Errorf("invalid network id passed while freeing overlay network")
+	}
+
+	d.Lock()
+	n, ok := d.networks[id]
+	d.Unlock()
+
+	if !ok {
+		return fmt.Errorf("overlay network with id %s not found", id)
+	}
+
+	// Release all vxlan IDs in one shot.
+	n.releaseVxlanID()
+
+	d.Lock()
+	delete(d.networks, id)
+	d.Unlock()
+
+	return nil
+}
+
+func (n *network) obtainVxlanID(s *subnet) error {
+	var (
+		err error
+		vni uint64
+	)
+
+	n.Lock()
+	vni = uint64(s.vni)
+	n.Unlock()
+
+	if vni == 0 {
+		vni, err = n.driver.vxlanIdm.GetID()
+		if err != nil {
+			return err
+		}
+
+		n.Lock()
+		s.vni = uint32(vni)
+		n.Unlock()
+		return nil
+	}
+
+	return n.driver.vxlanIdm.GetSpecificID(vni)
+}
+
+func (n *network) releaseVxlanID() {
+	n.Lock()
+	vnis := make([]uint32, 0, len(n.subnets))
+	for _, s := range n.subnets {
+		vnis = append(vnis, s.vni)
+		s.vni = 0
+	}
+	n.Unlock()
+
+	for _, vni := range vnis {
+		n.driver.vxlanIdm.Release(uint64(vni))
+	}
+}
+
+func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
+}
+
+func (d *driver) DeleteNetwork(nid string) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+func (d *driver) DeleteEndpoint(nid, eid string) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
+	return nil, types.NotImplementedErrorf("not implemented")
+}
+
+// Join method is invoked when a Sandbox is attached to an endpoint.
+func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+// Leave method is invoked when a Sandbox detaches from an endpoint.
+func (d *driver) Leave(nid, eid string) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+func (d *driver) Type() string {
+	return networkType
+}
+
+// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
+func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster
+func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
+	return types.NotImplementedErrorf("not implemented")
+}
+
+func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
+	return types.NotImplementedErrorf("not implemented")
+}

+ 89 - 0
libnetwork/drivers/solaris/overlay/ovmanager/ovmanager_test.go

@@ -0,0 +1,89 @@
+// +build solaris
+
+package ovmanager
+
+import (
+	"fmt"
+	"net"
+	"strings"
+	"testing"
+
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/idm"
+	"github.com/docker/libnetwork/netlabel"
+	"github.com/docker/libnetwork/types"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func newDriver(t *testing.T) *driver {
+	d := &driver{
+		networks: networkTable{},
+	}
+
+	vxlanIdm, err := idm.New(nil, "vxlan-id", vxlanIDStart, vxlanIDEnd)
+	require.NoError(t, err)
+
+	d.vxlanIdm = vxlanIdm
+	return d
+}
+
+func parseCIDR(t *testing.T, ipnet string) *net.IPNet {
+	subnet, err := types.ParseCIDR(ipnet)
+	require.NoError(t, err)
+	return subnet
+}
+
+func TestNetworkAllocateFree(t *testing.T) {
+	d := newDriver(t)
+
+	ipamData := []driverapi.IPAMData{
+		{
+			Pool: parseCIDR(t, "10.1.1.0/24"),
+		},
+		{
+			Pool: parseCIDR(t, "10.1.2.0/24"),
+		},
+	}
+
+	vals, err := d.NetworkAllocate("testnetwork", nil, ipamData, nil)
+	require.NoError(t, err)
+
+	vxlanIDs, ok := vals[netlabel.OverlayVxlanIDList]
+	assert.Equal(t, true, ok)
+	assert.Equal(t, 2, len(strings.Split(vxlanIDs, ",")))
+
+	err = d.NetworkFree("testnetwork")
+	require.NoError(t, err)
+}
+
+func TestNetworkAllocateUserDefinedVNIs(t *testing.T) {
+	d := newDriver(t)
+
+	ipamData := []driverapi.IPAMData{
+		{
+			Pool: parseCIDR(t, "10.1.1.0/24"),
+		},
+		{
+			Pool: parseCIDR(t, "10.1.2.0/24"),
+		},
+	}
+
+	options := make(map[string]string)
+	// Intentionally add mode vnis than subnets
+	options[netlabel.OverlayVxlanIDList] = fmt.Sprintf("%d,%d,%d", 256, 257, 258)
+
+	vals, err := d.NetworkAllocate("testnetwork", options, ipamData, nil)
+	require.NoError(t, err)
+
+	vxlanIDs, ok := vals[netlabel.OverlayVxlanIDList]
+	assert.Equal(t, true, ok)
+
+	// We should only get exactly the same number of vnis as
+	// subnets. No more, no less, even if we passed more vnis.
+	assert.Equal(t, 2, len(strings.Split(vxlanIDs, ",")))
+	assert.Equal(t, fmt.Sprintf("%d,%d", 256, 257), vxlanIDs)
+
+	err = d.NetworkFree("testnetwork")
+	require.NoError(t, err)
+}

+ 336 - 0
libnetwork/drivers/solaris/overlay/peerdb.go

@@ -0,0 +1,336 @@
+package overlay
+
+import (
+	"fmt"
+	"net"
+	"sync"
+
+	log "github.com/Sirupsen/logrus"
+)
+
+const ovPeerTable = "overlay_peer_table"
+
+type peerKey struct {
+	peerIP  net.IP
+	peerMac net.HardwareAddr
+}
+
+type peerEntry struct {
+	eid        string
+	vtep       net.IP
+	peerIPMask net.IPMask
+	inSandbox  bool
+	isLocal    bool
+}
+
+type peerMap struct {
+	mp map[string]peerEntry
+	sync.Mutex
+}
+
+type peerNetworkMap struct {
+	mp map[string]*peerMap
+	sync.Mutex
+}
+
+func (pKey peerKey) String() string {
+	return fmt.Sprintf("%s %s", pKey.peerIP, pKey.peerMac)
+}
+
+func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error {
+	ipB, err := state.Token(true, nil)
+	if err != nil {
+		return err
+	}
+
+	pKey.peerIP = net.ParseIP(string(ipB))
+
+	macB, err := state.Token(true, nil)
+	if err != nil {
+		return err
+	}
+
+	pKey.peerMac, err = net.ParseMAC(string(macB))
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+var peerDbWg sync.WaitGroup
+
+func (d *driver) peerDbWalk(f func(string, *peerKey, *peerEntry) bool) error {
+	d.peerDb.Lock()
+	nids := []string{}
+	for nid := range d.peerDb.mp {
+		nids = append(nids, nid)
+	}
+	d.peerDb.Unlock()
+
+	for _, nid := range nids {
+		d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
+			return f(nid, pKey, pEntry)
+		})
+	}
+	return nil
+}
+
+func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool) error {
+	d.peerDb.Lock()
+	pMap, ok := d.peerDb.mp[nid]
+	if !ok {
+		d.peerDb.Unlock()
+		return nil
+	}
+	d.peerDb.Unlock()
+
+	pMap.Lock()
+	for pKeyStr, pEntry := range pMap.mp {
+		var pKey peerKey
+		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
+			log.Warnf("Peer key scan on network %s failed: %v", nid, err)
+		}
+
+		if f(&pKey, &pEntry) {
+			pMap.Unlock()
+			return nil
+		}
+	}
+	pMap.Unlock()
+
+	return nil
+}
+
+func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
+	var (
+		peerMac    net.HardwareAddr
+		vtep       net.IP
+		peerIPMask net.IPMask
+		found      bool
+	)
+
+	err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
+		if pKey.peerIP.Equal(peerIP) {
+			peerMac = pKey.peerMac
+			peerIPMask = pEntry.peerIPMask
+			vtep = pEntry.vtep
+			found = true
+			return found
+		}
+
+		return found
+	})
+
+	if err != nil {
+		return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
+	}
+
+	if !found {
+		return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
+	}
+
+	return peerMac, peerIPMask, vtep, nil
+}
+
+func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
+	peerMac net.HardwareAddr, vtep net.IP, isLocal bool) {
+
+	peerDbWg.Wait()
+
+	d.peerDb.Lock()
+	pMap, ok := d.peerDb.mp[nid]
+	if !ok {
+		d.peerDb.mp[nid] = &peerMap{
+			mp: make(map[string]peerEntry),
+		}
+
+		pMap = d.peerDb.mp[nid]
+	}
+	d.peerDb.Unlock()
+
+	pKey := peerKey{
+		peerIP:  peerIP,
+		peerMac: peerMac,
+	}
+
+	pEntry := peerEntry{
+		eid:        eid,
+		vtep:       vtep,
+		peerIPMask: peerIPMask,
+		isLocal:    isLocal,
+	}
+
+	pMap.Lock()
+	pMap.mp[pKey.String()] = pEntry
+	pMap.Unlock()
+}
+
+func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
+	peerMac net.HardwareAddr, vtep net.IP) {
+	peerDbWg.Wait()
+
+	d.peerDb.Lock()
+	pMap, ok := d.peerDb.mp[nid]
+	if !ok {
+		d.peerDb.Unlock()
+		return
+	}
+	d.peerDb.Unlock()
+
+	pKey := peerKey{
+		peerIP:  peerIP,
+		peerMac: peerMac,
+	}
+
+	pMap.Lock()
+	delete(pMap.mp, pKey.String())
+	pMap.Unlock()
+}
+
+func (d *driver) peerDbUpdateSandbox(nid string) {
+	d.peerDb.Lock()
+	pMap, ok := d.peerDb.mp[nid]
+	if !ok {
+		d.peerDb.Unlock()
+		return
+	}
+	d.peerDb.Unlock()
+
+	peerDbWg.Add(1)
+
+	var peerOps []func()
+	pMap.Lock()
+	for pKeyStr, pEntry := range pMap.mp {
+		var pKey peerKey
+		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
+			fmt.Printf("peer key scan failed: %v", err)
+		}
+
+		if pEntry.isLocal {
+			continue
+		}
+
+		// Go captures variables by reference. The pEntry could be
+		// pointing to the same memory location for every iteration. Make
+		// a copy of pEntry before capturing it in the following closure.
+		entry := pEntry
+		op := func() {
+			if err := d.peerAdd(nid, entry.eid, pKey.peerIP, entry.peerIPMask,
+				pKey.peerMac, entry.vtep,
+				false); err != nil {
+				fmt.Printf("peerdbupdate in sandbox failed for ip %s and mac %s: %v",
+					pKey.peerIP, pKey.peerMac, err)
+			}
+		}
+
+		peerOps = append(peerOps, op)
+	}
+	pMap.Unlock()
+
+	for _, op := range peerOps {
+		op()
+	}
+
+	peerDbWg.Done()
+}
+
+func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
+	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {
+
+	if err := validateID(nid, eid); err != nil {
+		return err
+	}
+
+	if updateDb {
+		d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false)
+	}
+
+	n := d.network(nid)
+	if n == nil {
+		return nil
+	}
+
+	sbox := n.sandbox()
+	if sbox == nil {
+		return nil
+	}
+
+	IP := &net.IPNet{
+		IP:   peerIP,
+		Mask: peerIPMask,
+	}
+
+	s := n.getSubnetforIP(IP)
+	if s == nil {
+		return fmt.Errorf("couldn't find the subnet %q in network %q\n", IP.String(), n.id)
+	}
+
+	if err := n.obtainVxlanID(s); err != nil {
+		return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
+	}
+
+	if err := n.joinSubnetSandbox(s, false); err != nil {
+		return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
+	}
+
+	if err := d.checkEncryption(nid, vtep, n.vxlanID(s), false, true); err != nil {
+		log.Warn(err)
+	}
+
+	// Add neighbor entry for the peer IP
+	if err := sbox.AddNeighbor(peerIP, peerMac, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
+		return fmt.Errorf("could not add neigbor entry into the sandbox: %v", err)
+	}
+
+	// XXX Add fdb entry to the bridge for the peer mac
+
+	return nil
+}
+
+func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
+	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {
+
+	if err := validateID(nid, eid); err != nil {
+		return err
+	}
+
+	if updateDb {
+		d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep)
+	}
+
+	n := d.network(nid)
+	if n == nil {
+		return nil
+	}
+
+	sbox := n.sandbox()
+	if sbox == nil {
+		return nil
+	}
+
+	// Delete fdb entry to the bridge for the peer mac
+	if err := sbox.DeleteNeighbor(vtep, peerMac, true); err != nil {
+		return fmt.Errorf("could not delete fdb entry into the sandbox: %v", err)
+	}
+
+	// Delete neighbor entry for the peer IP
+	if err := sbox.DeleteNeighbor(peerIP, peerMac, true); err != nil {
+		return fmt.Errorf("could not delete neigbor entry into the sandbox: %v", err)
+	}
+
+	if err := d.checkEncryption(nid, vtep, 0, false, false); err != nil {
+		log.Warn(err)
+	}
+
+	return nil
+}
+
+func (d *driver) pushLocalDb() {
+	d.peerDbWalk(func(nid string, pKey *peerKey, pEntry *peerEntry) bool {
+		if pEntry.isLocal {
+			d.pushLocalEndpointEvent("join", nid, pEntry.eid)
+		}
+		return false
+	})
+}

+ 2 - 0
libnetwork/drivers_solaris.go

@@ -3,10 +3,12 @@ package libnetwork
 import (
 	"github.com/docker/libnetwork/drivers/null"
 	"github.com/docker/libnetwork/drivers/solaris/bridge"
+	"github.com/docker/libnetwork/drivers/solaris/overlay"
 )
 
 func getInitializers() []initializer {
 	return []initializer{
+		{overlay.Init, "overlay"},
 		{bridge.Init, "bridge"},
 		{null.Init, "null"},
 	}