-
Notifications
You must be signed in to change notification settings - Fork 229
/
ipset.go
408 lines (366 loc) · 11.6 KB
/
ipset.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
package ipsets
import (
"errors"
"fmt"
"github.com/Azure/azure-container-networking/log"
"github.com/Azure/azure-container-networking/npm/metrics"
"github.com/Azure/azure-container-networking/npm/util"
)
type IPSetMetadata struct {
Name string
Type SetType
}
type SetKind string
const (
// ListSet is of kind list with members as other IPSets
ListSet SetKind = "list"
// HashSet is of kind hashset with members as IPs and/or port
HashSet SetKind = "set"
// UnknownKind is returned when kind is unknown
UnknownKind SetKind = "unknown"
)
// NewIPSetMetadata is used for controllers to send in skeleton ipsets to DP
func NewIPSetMetadata(name string, setType SetType) *IPSetMetadata {
set := &IPSetMetadata{
Name: name,
Type: setType,
}
return set
}
func (setMetadata *IPSetMetadata) GetHashedName() string {
prefixedName := setMetadata.GetPrefixName()
if prefixedName == Unknown {
return Unknown
}
return util.GetHashedName(prefixedName)
}
// TODO join with colon instead of dash for easier readability?
func (setMetadata *IPSetMetadata) GetPrefixName() string {
switch setMetadata.Type {
case CIDRBlocks:
return fmt.Sprintf("%s%s", util.CIDRPrefix, setMetadata.Name)
case Namespace:
return fmt.Sprintf("%s%s", util.NamespacePrefix, setMetadata.Name)
case NamedPorts:
return fmt.Sprintf("%s%s", util.NamedPortIPSetPrefix, setMetadata.Name)
case KeyLabelOfPod:
return fmt.Sprintf("%s%s", util.PodLabelPrefix, setMetadata.Name)
case KeyValueLabelOfPod:
return fmt.Sprintf("%s%s", util.PodLabelPrefix, setMetadata.Name)
case KeyLabelOfNamespace:
return fmt.Sprintf("%s%s", util.NamespaceLabelPrefix, setMetadata.Name)
case KeyValueLabelOfNamespace:
return fmt.Sprintf("%s%s", util.NamespaceLabelPrefix, setMetadata.Name)
case NestedLabelOfPod:
return fmt.Sprintf("%s%s", util.NestedLabelPrefix, setMetadata.Name)
case EmptyHashSet:
return fmt.Sprintf("%s%s", util.EmptySetPrefix, setMetadata.Name)
case UnknownType: // adding this to appease golint
metrics.SendErrorLogAndMetric(util.UtilID, "experienced unknown type in set metadata: %+v", setMetadata)
return Unknown
default:
metrics.SendErrorLogAndMetric(util.UtilID, "experienced unexpected type %d in set metadata: %+v", setMetadata.Type, setMetadata)
return Unknown
}
}
func (setMetadata *IPSetMetadata) GetSetKind() SetKind {
return setMetadata.Type.getSetKind()
}
func (setType SetType) getSetKind() SetKind {
switch setType {
case CIDRBlocks:
return HashSet
case Namespace:
return HashSet
case NamedPorts:
return HashSet
case KeyLabelOfPod:
return HashSet
case KeyValueLabelOfPod:
return HashSet
case EmptyHashSet:
return HashSet
case KeyLabelOfNamespace:
return ListSet
case KeyValueLabelOfNamespace:
return ListSet
case NestedLabelOfPod:
return ListSet
case UnknownType: // adding this to appease golint
return UnknownKind
default:
return UnknownKind
}
}
// TranslatedIPSet is created by translation engine and provides IPSets used in
// network policy. Only 2 types of IPSets are generated with members:
// 1. CIDRBlocks IPSet
// 2. NestedLabelOfPod IPSet from multi value labels
// Members field holds member ipset names for NestedLabelOfPod and ip address ranges
// for CIDRBlocks IPSet
// Caveat: if a list set with translated members is referenced in multiple policies,
// then it must have a different ipset name for each policy. Otherwise, deleting the policy
// will result in removing the translated members from the set even if another policy requires
// those members. See dataplane.go for more details.
type TranslatedIPSet struct {
Metadata *IPSetMetadata
// Members holds member ipset names for NestedLabelOfPod and ip address ranges
// for CIDRBlocks IPSet
Members []string
}
// NewTranslatedIPSet creates TranslatedIPSet.
// Only nested labels from podSelector and IPBlock has members and others has nil slice.
func NewTranslatedIPSet(name string, setType SetType, members ...string) *TranslatedIPSet {
translatedIPSet := &TranslatedIPSet{
Metadata: NewIPSetMetadata(name, setType),
Members: members,
}
return translatedIPSet
}
type SetProperties struct {
// Stores type of ip grouping
Type SetType
// Stores kind of ipset in dataplane
Kind SetKind
}
type SetType int8
// Possble values for SetType
const (
// Unknown SetType
UnknownType SetType = 0
// Namespace IPSet is created to hold
// ips of pods in a given NameSapce
Namespace SetType = 1
// KeyLabelOfNamespace IPSet is a list kind ipset
// with members as ipsets of namespace with this Label Key
KeyLabelOfNamespace SetType = 2
// KeyValueLabelOfNamespace IPSet is a list kind ipset
// with members as ipsets of namespace with this Label
KeyValueLabelOfNamespace SetType = 3
// KeyLabelOfPod IPSet contains IPs of Pods with this Label Key
KeyLabelOfPod SetType = 4
// KeyValueLabelOfPod IPSet contains IPs of Pods with this Label
KeyValueLabelOfPod SetType = 5
// NamedPorts IPSets contains a given namedport
NamedPorts SetType = 6
// NestedLabelOfPod is derived for multivalue matchexpressions
NestedLabelOfPod SetType = 7
// CIDRBlocks holds CIDR blocks
CIDRBlocks SetType = 8
// EmptyHashSet is a set meant to have no members
EmptyHashSet SetType = 9
// Unknown const for unknown string
Unknown string = "unknown"
)
var (
setTypeName = map[SetType]string{
UnknownType: Unknown,
Namespace: "Namespace",
KeyLabelOfNamespace: "KeyLabelOfNamespace",
KeyValueLabelOfNamespace: "KeyValueLabelOfNamespace",
KeyLabelOfPod: "KeyLabelOfPod",
KeyValueLabelOfPod: "KeyValueLabelOfPod",
NamedPorts: "NamedPorts",
NestedLabelOfPod: "NestedLabelOfPod",
CIDRBlocks: "CIDRBlocks",
EmptyHashSet: "EmptySet",
}
// ErrIPSetInvalidKind is returned when IPSet kind is invalid
ErrIPSetInvalidKind = errors.New("invalid IPSet Kind")
)
func (x SetType) String() string {
return setTypeName[x]
}
// ReferenceType specifies the kind of reference for an IPSet
type ReferenceType string
// Possible ReferenceTypes
const (
SelectorType ReferenceType = "Selector"
NetPolType ReferenceType = "NetPol"
)
type IPSet struct {
// Name is prefixed name of original set
Name string
unprefixedName string
// HashedName is AzureNpmPrefix (azure-npm-) + hash of prefixed name
HashedName string
// SetProperties embedding set properties
SetProperties
// IpPodKey is used for setMaps to store Ips and ports as keys
// and podKey as value
IPPodKey map[string]string
// This is used for listMaps to store child IP Sets
MemberIPSets map[string]*IPSet
// Using a map to emulate set and value as struct{} for
// minimal memory consumption
// SelectorReference holds networkpolicy names where this IPSet
// is being used in PodSelector and Namespace
SelectorReference map[string]struct{}
// NetPolReference holds networkpolicy names where this IPSet
// is being referred as part of rules
NetPolReference map[string]struct{}
// ipsetReferCount keeps track of how many lists in the cache refer to this ipset
ipsetReferCount int
// kernelReferCount keeps track of how many lists in the kernel refer to this ipset
kernelReferCount int
}
func NewIPSet(setMetadata *IPSetMetadata) *IPSet {
prefixedName := setMetadata.GetPrefixName()
set := &IPSet{
Name: prefixedName,
unprefixedName: setMetadata.Name,
HashedName: util.GetHashedName(prefixedName),
SetProperties: SetProperties{
Type: setMetadata.Type,
Kind: setMetadata.GetSetKind(),
},
// Map with Key as Network Policy name to to emulate set
// and value as struct{} for minimal memory consumption
SelectorReference: make(map[string]struct{}),
// Map with Key as Network Policy name to to emulate set
// and value as struct{} for minimal memory consumption
NetPolReference: make(map[string]struct{}),
ipsetReferCount: 0,
kernelReferCount: 0,
}
if set.Kind == HashSet {
set.IPPodKey = make(map[string]string)
} else {
set.MemberIPSets = make(map[string]*IPSet)
}
return set
}
// GetSetMetadata returns set metadata with unprefixed original name and SetType
func (set *IPSet) GetSetMetadata() *IPSetMetadata {
return NewIPSetMetadata(set.unprefixedName, set.Type)
}
func (set *IPSet) PrettyString() string {
return fmt.Sprintf("Name: %s HashedNamed: %s Type: %s Kind: %s",
set.Name, set.HashedName, setTypeName[set.Type], string(set.Kind))
}
// GetSetContents returns members of set as string slice
func (set *IPSet) GetSetContents() ([]string, error) {
switch set.Kind {
case HashSet:
i := 0
contents := make([]string, len(set.IPPodKey))
for podIP := range set.IPPodKey {
contents[i] = podIP
i++
}
return contents, nil
case ListSet:
i := 0
contents := make([]string, len(set.MemberIPSets))
for _, memberSet := range set.MemberIPSets {
contents[i] = memberSet.HashedName
i++
}
return contents, nil
default:
return []string{}, ErrIPSetInvalidKind
}
}
// ShallowCompare check if the properties of IPSets are same
func (set *IPSet) ShallowCompare(newSet *IPSet) bool {
if set.Name != newSet.Name {
return false
}
if set.Kind != newSet.Kind {
return false
}
if set.Type != newSet.Type {
return false
}
return true
}
func (set *IPSet) incIPSetReferCount() {
set.ipsetReferCount++
}
func (set *IPSet) decIPSetReferCount() {
if set.ipsetReferCount == 0 {
return
}
set.ipsetReferCount--
}
func (set *IPSet) incKernelReferCount() {
set.kernelReferCount++
}
func (set *IPSet) decKernelReferCount() {
if set.kernelReferCount == 0 {
return
}
set.kernelReferCount--
}
func (set *IPSet) addReference(referenceName string, referenceType ReferenceType) {
switch referenceType {
case SelectorType:
set.SelectorReference[referenceName] = struct{}{}
case NetPolType:
set.NetPolReference[referenceName] = struct{}{}
default:
log.Logf("IPSet_addReference: encountered unknown ReferenceType")
}
}
func (set *IPSet) deleteReference(referenceName string, referenceType ReferenceType) {
switch referenceType {
case SelectorType:
delete(set.SelectorReference, referenceName)
case NetPolType:
delete(set.NetPolReference, referenceName)
default:
log.Logf("IPSet_deleteReference: encountered unknown ReferenceType")
}
}
func (set *IPSet) shouldBeInKernel() bool {
return set.usedByNetPol() || set.referencedInKernel()
}
func (set *IPSet) canBeForceDeleted() bool {
return !set.usedByNetPol() &&
!set.referencedInList()
}
func (set *IPSet) canBeDeleted(ignorableMember *IPSet) bool {
var firstMember *IPSet
for _, member := range set.MemberIPSets {
firstMember = member
break
}
listMembersOk := len(set.MemberIPSets) == 0 || (len(set.MemberIPSets) == 1 && firstMember == ignorableMember)
return !set.usedByNetPol() &&
!set.referencedInList() &&
len(set.IPPodKey) == 0 &&
listMembersOk
}
// usedByNetPol check if an IPSet is referred in network policies.
func (set *IPSet) usedByNetPol() bool {
return len(set.SelectorReference) > 0 ||
len(set.NetPolReference) > 0
}
func (set *IPSet) referencedInList() bool {
return set.ipsetReferCount > 0
}
func (set *IPSet) referencedInKernel() bool {
return set.kernelReferCount > 0
}
// panics if set is not a list set
func (set *IPSet) hasMember(memberName string) bool {
_, isMember := set.MemberIPSets[memberName]
return isMember
}
func (set *IPSet) canSetBeSelectorIPSet() bool {
return (set.Type == KeyLabelOfPod ||
set.Type == KeyValueLabelOfPod ||
set.Type == Namespace ||
set.Type == NestedLabelOfPod)
}
func GetMembersOfTranslatedSets(members []string) []*IPSetMetadata {
memberList := make([]*IPSetMetadata, len(members))
i := 0
for _, setName := range members {
// translate engine only returns KeyValueLabelOfPod as member
memberSet := NewIPSetMetadata(setName, KeyValueLabelOfPod)
memberList[i] = memberSet
i++
}
return memberList
}