1package ipam
2
3import (
4	"encoding/json"
5	"fmt"
6	"net"
7	"strings"
8	"sync"
9
10	"github.com/docker/libnetwork/datastore"
11	"github.com/docker/libnetwork/ipamapi"
12	"github.com/docker/libnetwork/types"
13)
14
15// SubnetKey is the pointer to the configured pools in each address space
16type SubnetKey struct {
17	AddressSpace string
18	Subnet       string
19	ChildSubnet  string
20}
21
22// PoolData contains the configured pool data
23type PoolData struct {
24	ParentKey SubnetKey
25	Pool      *net.IPNet
26	Range     *AddressRange `json:",omitempty"`
27	RefCount  int
28}
29
30// addrSpace contains the pool configurations for the address space
31type addrSpace struct {
32	subnets  map[SubnetKey]*PoolData
33	dbIndex  uint64
34	dbExists bool
35	id       string
36	scope    string
37	ds       datastore.DataStore
38	alloc    *Allocator
39	sync.Mutex
40}
41
42// AddressRange specifies first and last ip ordinal which
43// identifies a range in a pool of addresses
44type AddressRange struct {
45	Sub        *net.IPNet
46	Start, End uint64
47}
48
49// String returns the string form of the AddressRange object
50func (r *AddressRange) String() string {
51	return fmt.Sprintf("Sub: %s, range [%d, %d]", r.Sub, r.Start, r.End)
52}
53
54// MarshalJSON returns the JSON encoding of the Range object
55func (r *AddressRange) MarshalJSON() ([]byte, error) {
56	m := map[string]interface{}{
57		"Sub":   r.Sub.String(),
58		"Start": r.Start,
59		"End":   r.End,
60	}
61	return json.Marshal(m)
62}
63
64// UnmarshalJSON decodes data into the Range object
65func (r *AddressRange) UnmarshalJSON(data []byte) error {
66	m := map[string]interface{}{}
67	err := json.Unmarshal(data, &m)
68	if err != nil {
69		return err
70	}
71	if r.Sub, err = types.ParseCIDR(m["Sub"].(string)); err != nil {
72		return err
73	}
74	r.Start = uint64(m["Start"].(float64))
75	r.End = uint64(m["End"].(float64))
76	return nil
77}
78
79// String returns the string form of the SubnetKey object
80func (s *SubnetKey) String() string {
81	k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet)
82	if s.ChildSubnet != "" {
83		k = fmt.Sprintf("%s/%s", k, s.ChildSubnet)
84	}
85	return k
86}
87
88// FromString populates the SubnetKey object reading it from string
89func (s *SubnetKey) FromString(str string) error {
90	if str == "" || !strings.Contains(str, "/") {
91		return types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
92	}
93
94	p := strings.Split(str, "/")
95	if len(p) != 3 && len(p) != 5 {
96		return types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
97	}
98	s.AddressSpace = p[0]
99	s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2])
100	if len(p) == 5 {
101		s.ChildSubnet = fmt.Sprintf("%s/%s", p[3], p[4])
102	}
103
104	return nil
105}
106
107// String returns the string form of the PoolData object
108func (p *PoolData) String() string {
109	return fmt.Sprintf("ParentKey: %s, Pool: %s, Range: %s, RefCount: %d",
110		p.ParentKey.String(), p.Pool.String(), p.Range, p.RefCount)
111}
112
113// MarshalJSON returns the JSON encoding of the PoolData object
114func (p *PoolData) MarshalJSON() ([]byte, error) {
115	m := map[string]interface{}{
116		"ParentKey": p.ParentKey,
117		"RefCount":  p.RefCount,
118	}
119	if p.Pool != nil {
120		m["Pool"] = p.Pool.String()
121	}
122	if p.Range != nil {
123		m["Range"] = p.Range
124	}
125	return json.Marshal(m)
126}
127
128// UnmarshalJSON decodes data into the PoolData object
129func (p *PoolData) UnmarshalJSON(data []byte) error {
130	var (
131		err error
132		t   struct {
133			ParentKey SubnetKey
134			Pool      string
135			Range     *AddressRange `json:",omitempty"`
136			RefCount  int
137		}
138	)
139
140	if err = json.Unmarshal(data, &t); err != nil {
141		return err
142	}
143
144	p.ParentKey = t.ParentKey
145	p.Range = t.Range
146	p.RefCount = t.RefCount
147	if t.Pool != "" {
148		if p.Pool, err = types.ParseCIDR(t.Pool); err != nil {
149			return err
150		}
151	}
152
153	return nil
154}
155
156// MarshalJSON returns the JSON encoding of the addrSpace object
157func (aSpace *addrSpace) MarshalJSON() ([]byte, error) {
158	aSpace.Lock()
159	defer aSpace.Unlock()
160
161	m := map[string]interface{}{
162		"Scope": string(aSpace.scope),
163	}
164
165	if aSpace.subnets != nil {
166		s := map[string]*PoolData{}
167		for k, v := range aSpace.subnets {
168			s[k.String()] = v
169		}
170		m["Subnets"] = s
171	}
172
173	return json.Marshal(m)
174}
175
176// UnmarshalJSON decodes data into the addrSpace object
177func (aSpace *addrSpace) UnmarshalJSON(data []byte) error {
178	aSpace.Lock()
179	defer aSpace.Unlock()
180
181	m := map[string]interface{}{}
182	err := json.Unmarshal(data, &m)
183	if err != nil {
184		return err
185	}
186
187	aSpace.scope = datastore.LocalScope
188	s := m["Scope"].(string)
189	if s == string(datastore.GlobalScope) {
190		aSpace.scope = datastore.GlobalScope
191	}
192
193	if v, ok := m["Subnets"]; ok {
194		sb, _ := json.Marshal(v)
195		var s map[string]*PoolData
196		err := json.Unmarshal(sb, &s)
197		if err != nil {
198			return err
199		}
200		for ks, v := range s {
201			k := SubnetKey{}
202			k.FromString(ks)
203			aSpace.subnets[k] = v
204		}
205	}
206
207	return nil
208}
209
210// CopyTo deep copies the pool data to the destination pooldata
211func (p *PoolData) CopyTo(dstP *PoolData) error {
212	dstP.ParentKey = p.ParentKey
213	dstP.Pool = types.GetIPNetCopy(p.Pool)
214
215	if p.Range != nil {
216		dstP.Range = &AddressRange{}
217		dstP.Range.Sub = types.GetIPNetCopy(p.Range.Sub)
218		dstP.Range.Start = p.Range.Start
219		dstP.Range.End = p.Range.End
220	}
221
222	dstP.RefCount = p.RefCount
223	return nil
224}
225
226func (aSpace *addrSpace) CopyTo(o datastore.KVObject) error {
227	aSpace.Lock()
228	defer aSpace.Unlock()
229
230	dstAspace := o.(*addrSpace)
231
232	dstAspace.id = aSpace.id
233	dstAspace.ds = aSpace.ds
234	dstAspace.alloc = aSpace.alloc
235	dstAspace.scope = aSpace.scope
236	dstAspace.dbIndex = aSpace.dbIndex
237	dstAspace.dbExists = aSpace.dbExists
238
239	dstAspace.subnets = make(map[SubnetKey]*PoolData)
240	for k, v := range aSpace.subnets {
241		dstAspace.subnets[k] = &PoolData{}
242		v.CopyTo(dstAspace.subnets[k])
243	}
244
245	return nil
246}
247
248func (aSpace *addrSpace) New() datastore.KVObject {
249	aSpace.Lock()
250	defer aSpace.Unlock()
251
252	return &addrSpace{
253		id:    aSpace.id,
254		ds:    aSpace.ds,
255		alloc: aSpace.alloc,
256		scope: aSpace.scope,
257	}
258}
259
260// updatePoolDBOnAdd returns a closure which will add the subnet k to the address space when executed.
261func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange, pdf bool) (func() error, error) {
262	aSpace.Lock()
263	defer aSpace.Unlock()
264
265	// Check if already allocated
266	if _, ok := aSpace.subnets[k]; ok {
267		if pdf {
268			return nil, types.InternalMaskableErrorf("predefined pool %s is already reserved", nw)
269		}
270		// This means the same pool is already allocated. updatePoolDBOnAdd is called when there
271		// is request for a pool/subpool. It should ensure there is no overlap with existing pools
272		return nil, ipamapi.ErrPoolOverlap
273	}
274
275	// If master pool, check for overlap
276	if ipr == nil {
277		if aSpace.contains(k.AddressSpace, nw) {
278			return nil, ipamapi.ErrPoolOverlap
279		}
280		// This is a new master pool, add it along with corresponding bitmask
281		aSpace.subnets[k] = &PoolData{Pool: nw, RefCount: 1}
282		return func() error { return aSpace.alloc.insertBitMask(k, nw) }, nil
283	}
284
285	// This is a new non-master pool (subPool)
286	p := &PoolData{
287		ParentKey: SubnetKey{AddressSpace: k.AddressSpace, Subnet: k.Subnet},
288		Pool:      nw,
289		Range:     ipr,
290		RefCount:  1,
291	}
292	aSpace.subnets[k] = p
293
294	// Look for parent pool
295	pp, ok := aSpace.subnets[p.ParentKey]
296	if ok {
297		aSpace.incRefCount(pp, 1)
298		return func() error { return nil }, nil
299	}
300
301	// Parent pool does not exist, add it along with corresponding bitmask
302	aSpace.subnets[p.ParentKey] = &PoolData{Pool: nw, RefCount: 1}
303	return func() error { return aSpace.alloc.insertBitMask(p.ParentKey, nw) }, nil
304}
305
306func (aSpace *addrSpace) updatePoolDBOnRemoval(k SubnetKey) (func() error, error) {
307	aSpace.Lock()
308	defer aSpace.Unlock()
309
310	p, ok := aSpace.subnets[k]
311	if !ok {
312		return nil, ipamapi.ErrBadPool
313	}
314
315	aSpace.incRefCount(p, -1)
316
317	c := p
318	for ok {
319		if c.RefCount == 0 {
320			delete(aSpace.subnets, k)
321			if c.Range == nil {
322				return func() error {
323					bm, err := aSpace.alloc.retrieveBitmask(k, c.Pool)
324					if err != nil {
325						return types.InternalErrorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
326					}
327					return bm.Destroy()
328				}, nil
329			}
330		}
331		k = c.ParentKey
332		c, ok = aSpace.subnets[k]
333	}
334
335	return func() error { return nil }, nil
336}
337
338func (aSpace *addrSpace) incRefCount(p *PoolData, delta int) {
339	c := p
340	ok := true
341	for ok {
342		c.RefCount += delta
343		c, ok = aSpace.subnets[c.ParentKey]
344	}
345}
346
347// Checks whether the passed subnet is a superset or subset of any of the subset in this config db
348func (aSpace *addrSpace) contains(space string, nw *net.IPNet) bool {
349	for k, v := range aSpace.subnets {
350		if space == k.AddressSpace && k.ChildSubnet == "" {
351			if nw.Contains(v.Pool.IP) || v.Pool.Contains(nw.IP) {
352				return true
353			}
354		}
355	}
356	return false
357}
358
359func (aSpace *addrSpace) store() datastore.DataStore {
360	aSpace.Lock()
361	defer aSpace.Unlock()
362
363	return aSpace.ds
364}
365