/
cidr.go
150 lines (126 loc) · 5.57 KB
/
cidr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2018-2019 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ipcache
import (
"context"
"fmt"
"net"
"github.com/cilium/cilium/pkg/identity"
"github.com/cilium/cilium/pkg/identity/cache"
"github.com/cilium/cilium/pkg/ip"
"github.com/cilium/cilium/pkg/labels"
"github.com/cilium/cilium/pkg/labels/cidr"
"github.com/cilium/cilium/pkg/option"
"github.com/cilium/cilium/pkg/source"
)
var (
// IdentityAllocator is a package-level variable which is used to allocate
// identities for CIDRs.
// TODO: plumb an allocator in from callers of these functions vs. having
// this as a package-level variable.
IdentityAllocator *cache.CachingIdentityAllocator
)
// AllocateCIDRs attempts to allocate identities for a list of CIDRs. If any
// allocation fails, all allocations are rolled back and the error is returned.
// When an identity is freshly allocated for a CIDR, it is added to the
// ipcache if 'newlyAllocatedIdentities' is 'nil', otherwise the newly allocated
// identities are placed in 'newlyAllocatedIdentities' and it is the caller's
// responsibility to upsert them into ipcache by calling UpsertGeneratedIdentities().
func AllocateCIDRs(prefixes []*net.IPNet, newlyAllocatedIdentities map[string]*identity.Identity) ([]*identity.Identity, error) {
return allocateCIDRs(prefixes, newlyAllocatedIdentities)
}
// AllocateCIDRsForIPs attempts to allocate identities for a list of CIDRs. If
// any allocation fails, all allocations are rolled back and the error is
// returned. When an identity is freshly allocated for a CIDR, it is added to
// the ipcache if 'newlyAllocatedIdentities' is 'nil', otherwise the newly allocated
// identities are placed in 'newlyAllocatedIdentities' and it is the caller's
// responsibility to upsert them into ipcache by calling UpsertGeneratedIdentities().
func AllocateCIDRsForIPs(prefixes []net.IP, newlyAllocatedIdentities map[string]*identity.Identity) ([]*identity.Identity, error) {
return allocateCIDRs(ip.GetCIDRPrefixesFromIPs(prefixes), newlyAllocatedIdentities)
}
func UpsertGeneratedIdentities(newlyAllocatedIdentities map[string]*identity.Identity) {
for prefixString, id := range newlyAllocatedIdentities {
IPIdentityCache.Upsert(prefixString, nil, 0, nil, Identity{
ID: id.ID,
Source: source.Generated,
})
}
}
func allocateCIDRs(prefixes []*net.IPNet, newlyAllocatedIdentities map[string]*identity.Identity) ([]*identity.Identity, error) {
// maintain list of used identities to undo on error
usedIdentities := make([]*identity.Identity, 0, len(prefixes))
allocatedIdentities := make(map[string]*identity.Identity, len(prefixes))
// Maintain list of newly allocated identities to update ipcache,
// but upsert them to ipcache only if no map was given by the caller.
upsert := false
if newlyAllocatedIdentities == nil {
upsert = true
newlyAllocatedIdentities = map[string]*identity.Identity{}
}
for _, prefix := range prefixes {
if prefix == nil {
continue
}
prefixStr := prefix.String()
// Figure out if this call needs to be able to update the selector cache synchronously.
allocateCtx, cancel := context.WithTimeout(context.Background(), option.Config.IPAllocationTimeout)
defer cancel()
if IdentityAllocator == nil {
return nil, fmt.Errorf("IdentityAllocator not initialized!")
}
id, isNew, err := IdentityAllocator.AllocateIdentity(allocateCtx, cidr.GetCIDRLabels(prefix), false)
if err != nil {
IdentityAllocator.ReleaseSlice(context.Background(), nil, usedIdentities)
return nil, fmt.Errorf("failed to allocate identity for cidr %s: %s", prefixStr, err)
}
id.CIDRLabel = labels.NewLabelsFromModel([]string{labels.LabelSourceCIDR + ":" + prefixStr})
usedIdentities = append(usedIdentities, id)
allocatedIdentities[prefixStr] = id
if isNew {
newlyAllocatedIdentities[prefixStr] = id
}
}
allocatedIdentitiesSlice := make([]*identity.Identity, 0, len(allocatedIdentities))
// Only upsert into ipcache if identity wasn't allocated
// before and the caller does not care doing this
if upsert {
UpsertGeneratedIdentities(newlyAllocatedIdentities)
}
for _, id := range allocatedIdentities {
allocatedIdentitiesSlice = append(allocatedIdentitiesSlice, id)
}
return allocatedIdentitiesSlice, nil
}
// ReleaseCIDRs releases the identities of a list of CIDRs. When the last use
// of the identity is released, the ipcache entry is deleted.
func ReleaseCIDRs(prefixes []*net.IPNet) {
for _, prefix := range prefixes {
if prefix == nil {
continue
}
if id := IdentityAllocator.LookupIdentity(context.TODO(), cidr.GetCIDRLabels(prefix)); id != nil {
releaseCtx, cancel := context.WithTimeout(context.Background(), option.Config.KVstoreConnectivityTimeout)
defer cancel()
released, err := IdentityAllocator.Release(releaseCtx, id)
if err != nil {
log.WithError(err).Warningf("Unable to release identity for CIDR %s. Ignoring error. Identity may be leaked", prefix.String())
}
if released {
IPIdentityCache.Delete(prefix.String(), source.Generated)
}
} else {
log.Errorf("Unable to find identity of previously used CIDR %s", prefix.String())
}
}
}