package ovmanager import ( "fmt" "net" "strconv" "strings" "sync" "github.com/docker/docker/libnetwork/datastore" "github.com/docker/docker/libnetwork/discoverapi" "github.com/docker/docker/libnetwork/driverapi" "github.com/docker/docker/libnetwork/idm" "github.com/docker/docker/libnetwork/netlabel" "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) const ( networkType = "overlay" vxlanIDStart = 4096 vxlanIDEnd = (1 << 24) - 1 ) type networkTable map[string]*network type driver struct { config map[string]interface{} networks networkTable 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, ConnectivityScope: datastore.GlobalScope, } d := &driver{ networks: networkTable{}, config: config, } d.vxlanIdm, err = idm.New(nil, "vxlan-id", 0, 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() defer d.Unlock() if _, ok := d.networks[id]; ok { n.releaseVxlanID() return nil, fmt.Errorf("network %s already exists", id) } d.networks[id] = n 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() defer d.Unlock() n, ok := d.networks[id] if !ok { return fmt.Errorf("overlay network with id %s not found", id) } // Release all vxlan IDs in one shot. n.releaseVxlanID() delete(d.networks, id) 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.GetIDInRange(vxlanIDStart, vxlanIDEnd, true) 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) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { return "", nil } 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 } func (d *driver) IsBuiltIn() bool { return true } // 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") }