-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
policymap.go
465 lines (391 loc) · 14.1 KB
/
policymap.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
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium
package policymap
import (
"fmt"
"strconv"
"strings"
"unsafe"
"github.com/cilium/cilium/pkg/bpf"
"github.com/cilium/cilium/pkg/byteorder"
"github.com/cilium/cilium/pkg/policy/trafficdirection"
"github.com/cilium/cilium/pkg/u8proto"
)
const (
// PolicyCallMapName is the name of the map to do tail calls into policy
// enforcement programs.
PolicyCallMapName = "cilium_call_policy"
// PolicyEgressCallMapName is the name of the map to do tail calls into egress policy
// enforcement programs.
PolicyEgressCallMapName = "cilium_egresscall_policy"
// MapName is the prefix for endpoint-specific policy maps which map
// identity+ports+direction to whether the policy allows communication
// with that identity on that port for that direction.
MapName = "cilium_policy_"
// PolicyCallMaxEntries is the upper limit of entries in the program
// array for the tail calls to jump into the endpoint specific policy
// programs. This number *MUST* be identical to the maximum endpoint ID.
PolicyCallMaxEntries = ^uint16(0)
// AllPorts is used to ignore the L4 ports in PolicyMap lookups; all ports
// are allowed. In the datapath, this is represented with the value 0 in the
// port field of map elements.
AllPorts = uint16(0)
// PressureMetricThreshold sets the threshold over which map pressure will
// be reported for the policy map.
PressureMetricThreshold = 0.1
)
type policyFlag uint8
const (
policyFlagDeny = 1 << iota
)
// PolicyEntryFlags is a new type used to define the flags used in the policy
// entry.
type PolicyEntryFlags uint8
// UInt8 returns the UInt8 representation of the PolicyEntryFlags.
func (pef PolicyEntryFlags) UInt8() uint8 {
return uint8(pef)
}
func (pef PolicyEntryFlags) is(pf policyFlag) bool {
return uint8(pef)&uint8(pf) != 0
}
func (pef PolicyEntryFlags) IsDeny() bool {
return pef.is(policyFlagDeny)
}
// String returns the string implementation of PolicyEntryFlags.
func (pef PolicyEntryFlags) String() string {
if pef.IsDeny() {
return "Deny"
}
return "Allow"
}
var (
// MaxEntries is the upper limit of entries in the per endpoint policy
// table ie the maximum number of peer identities that the endpoint could
// send/receive traffic to/from.. It is set by InitMapInfo(), but unit
// tests use the initial value below.
// The default value of this upper limit is 16384.
MaxEntries = 16384
)
type PolicyMap struct {
*bpf.Map
}
func (pe *PolicyEntry) String() string {
return fmt.Sprintf("%d %d %d", pe.ProxyPort, pe.Packets, pe.Bytes)
}
// PolicyKey represents a key in the BPF policy map for an endpoint. It must
// match the layout of policy_key in bpf/lib/common.h.
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=github.com/cilium/cilium/pkg/bpf.MapKey
type PolicyKey struct {
Identity uint32 `align:"sec_label"`
DestPort uint16 `align:"dport"` // In network byte-order
Nexthdr uint8 `align:"protocol"`
TrafficDirection uint8 `align:"egress"`
}
// SizeofPolicyKey is the size of type PolicyKey.
const SizeofPolicyKey = int(unsafe.Sizeof(PolicyKey{}))
// PolicyEntry represents an entry in the BPF policy map for an endpoint. It must
// match the layout of policy_entry in bpf/lib/common.h.
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=github.com/cilium/cilium/pkg/bpf.MapValue
type PolicyEntry struct {
ProxyPort uint16 `align:"proxy_port"` // In network byte-order
Flags uint8 `align:"deny"`
Pad0 uint8 `align:"pad0"`
Pad1 uint16 `align:"pad1"`
Pad2 uint16 `align:"pad2"`
Packets uint64 `align:"packets"`
Bytes uint64 `align:"bytes"`
}
// ToHost returns a copy of entry with fields converted from network byte-order
// to host-byte-order if necessary.
func (pe *PolicyEntry) ToHost() PolicyEntry {
if pe == nil {
return PolicyEntry{}
}
n := *pe
n.ProxyPort = byteorder.NetworkToHost16(n.ProxyPort)
return n
}
func (pe *PolicyEntry) SetFlags(flags uint8) {
pe.Flags = flags
}
func (pe *PolicyEntry) GetFlags() uint8 {
return pe.Flags
}
type PolicyEntryFlagParam struct {
IsDeny bool
}
// NewPolicyEntryFlag returns a PolicyEntryFlags from the PolicyEntryFlagParam.
func NewPolicyEntryFlag(p *PolicyEntryFlagParam) PolicyEntryFlags {
var flags PolicyEntryFlags
if p.IsDeny {
flags |= policyFlagDeny
}
return flags
}
// SizeofPolicyEntry is the size of type PolicyEntry.
const SizeofPolicyEntry = int(unsafe.Sizeof(PolicyEntry{}))
// CallKey is the index into the prog array map.
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=github.com/cilium/cilium/pkg/bpf.MapKey
type CallKey struct {
index uint32
}
// CallValue is the program ID in the prog array map.
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=github.com/cilium/cilium/pkg/bpf.MapValue
type CallValue struct {
progID uint32
}
// GetKeyPtr returns the unsafe pointer to the BPF key
func (k *CallKey) GetKeyPtr() unsafe.Pointer { return unsafe.Pointer(k) }
// GetValuePtr returns the unsafe pointer to the BPF value
func (v *CallValue) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(v) }
// String converts the key into a human readable string format.
func (k *CallKey) String() string { return strconv.FormatUint(uint64(k.index), 10) }
// String converts the value into a human readable string format.
func (v *CallValue) String() string { return strconv.FormatUint(uint64(v.progID), 10) }
// NewValue returns a new empty instance of the structure representing the BPF
// map value.
func (k CallKey) NewValue() bpf.MapValue { return &CallValue{} }
func (pe *PolicyEntry) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(pe) }
func (pe *PolicyEntry) NewValue() bpf.MapValue { return &PolicyEntry{} }
func (pe *PolicyEntry) Add(oPe PolicyEntry) {
pe.Packets += oPe.Packets
pe.Bytes += oPe.Bytes
}
type PolicyEntryDump struct {
PolicyEntry
Key PolicyKey
}
// PolicyEntriesDump is a wrapper for a slice of PolicyEntryDump
type PolicyEntriesDump []PolicyEntryDump
// String returns a string representation of PolicyEntriesDump
func (p PolicyEntriesDump) String() string {
var sb strings.Builder
for _, entry := range p {
sb.WriteString(fmt.Sprintf("%20s: %s\n",
entry.Key.String(), entry.PolicyEntry.String()))
}
return sb.String()
}
// Less is a function used to sort PolicyEntriesDump by Policy Type
// (Deny / Allow), TrafficDirection (Ingress / Egress) and Identity
// (ascending order).
func (p PolicyEntriesDump) Less(i, j int) bool {
iDeny := PolicyEntryFlags(p[i].PolicyEntry.GetFlags()).IsDeny()
jDeny := PolicyEntryFlags(p[j].PolicyEntry.GetFlags()).IsDeny()
switch {
case iDeny && !jDeny:
return true
case !iDeny && jDeny:
return false
}
if p[i].Key.TrafficDirection < p[j].Key.TrafficDirection {
return true
}
return p[i].Key.TrafficDirection <= p[j].Key.TrafficDirection &&
p[i].Key.Identity < p[j].Key.Identity
}
func (key *PolicyKey) GetKeyPtr() unsafe.Pointer { return unsafe.Pointer(key) }
func (key *PolicyKey) NewValue() bpf.MapValue { return &PolicyEntry{} }
func (key *PolicyKey) String() string {
trafficDirectionString := (trafficdirection.TrafficDirection)(key.TrafficDirection).String()
if key.DestPort != 0 {
return fmt.Sprintf("%s: %d %d/%d", trafficDirectionString, key.Identity, byteorder.NetworkToHost16(key.DestPort), key.Nexthdr)
}
return fmt.Sprintf("%s: %d", trafficDirectionString, key.Identity)
}
// ToHost returns a copy of key with fields converted from network byte-order
// to host-byte-order if necessary.
func (key *PolicyKey) ToHost() PolicyKey {
if key == nil {
return PolicyKey{}
}
n := *key
n.DestPort = byteorder.NetworkToHost16(n.DestPort)
return n
}
// ToNetwork returns a copy of key with fields converted from host byte-order
// to network-byte-order if necessary.
func (key *PolicyKey) ToNetwork() PolicyKey {
if key == nil {
return PolicyKey{}
}
n := *key
n.DestPort = byteorder.HostToNetwork16(n.DestPort)
return n
}
// newKey returns a PolicyKey representing the specified parameters in network
// byte-order.
func newKey(id uint32, dport uint16, proto u8proto.U8proto, trafficDirection trafficdirection.TrafficDirection) PolicyKey {
return PolicyKey{
Identity: id,
DestPort: byteorder.HostToNetwork16(dport),
Nexthdr: uint8(proto),
TrafficDirection: trafficDirection.Uint8(),
}
}
// newEntry returns a PolicyEntry representing the specified parameters in
// network byte-order.
func newEntry(proxyPort uint16, flags PolicyEntryFlags) PolicyEntry {
return PolicyEntry{
ProxyPort: byteorder.HostToNetwork16(proxyPort),
Flags: flags.UInt8(),
}
}
// AllowKey pushes an entry into the PolicyMap for the given PolicyKey k.
// Returns an error if the update of the PolicyMap fails.
func (pm *PolicyMap) AllowKey(k PolicyKey, proxyPort uint16) error {
return pm.Allow(k.Identity, k.DestPort, u8proto.U8proto(k.Nexthdr), trafficdirection.TrafficDirection(k.TrafficDirection), proxyPort)
}
// Allow pushes an entry into the PolicyMap to allow traffic in the given
// `trafficDirection` for identity `id` with destination port `dport` over
// protocol `proto`. It is assumed that `dport` and `proxyPort` are in host byte-order.
func (pm *PolicyMap) Allow(id uint32, dport uint16, proto u8proto.U8proto, trafficDirection trafficdirection.TrafficDirection, proxyPort uint16) error {
key := newKey(id, dport, proto, trafficDirection)
pef := NewPolicyEntryFlag(&PolicyEntryFlagParam{})
entry := newEntry(proxyPort, pef)
return pm.Update(&key, &entry)
}
// DenyKey pushes an entry into the PolicyMap for the given PolicyKey k.
// Returns an error if the update of the PolicyMap fails.
func (pm *PolicyMap) DenyKey(k PolicyKey) error {
return pm.Deny(k.Identity, k.DestPort, u8proto.U8proto(k.Nexthdr), trafficdirection.TrafficDirection(k.TrafficDirection))
}
// Deny pushes an entry into the PolicyMap to deny traffic in the given
// `trafficDirection` for identity `id` with destination port `dport` over
// protocol `proto`. It is assumed that `dport` is in host byte-order.
func (pm *PolicyMap) Deny(id uint32, dport uint16, proto u8proto.U8proto, trafficDirection trafficdirection.TrafficDirection) error {
key := newKey(id, dport, proto, trafficDirection)
pef := NewPolicyEntryFlag(&PolicyEntryFlagParam{IsDeny: true})
entry := newEntry(0, pef)
return pm.Update(&key, &entry)
}
// Exists determines whether PolicyMap currently contains an entry that
// allows traffic in `trafficDirection` for identity `id` with destination port
// `dport`over protocol `proto`. It is assumed that `dport` is in host byte-order.
func (pm *PolicyMap) Exists(id uint32, dport uint16, proto u8proto.U8proto, trafficDirection trafficdirection.TrafficDirection) bool {
key := newKey(id, dport, proto, trafficDirection)
_, err := pm.Lookup(&key)
return err == nil
}
// DeleteKey deletes the key-value pair from the given PolicyMap with PolicyKey
// k. Returns an error if deletion from the PolicyMap fails.
func (pm *PolicyMap) DeleteKey(key PolicyKey) error {
k := key.ToNetwork()
return pm.Map.Delete(&k)
}
// Delete removes an entry from the PolicyMap for identity `id`
// sending traffic in direction `trafficDirection` with destination port `dport`
// over protocol `proto`. It is assumed that `dport` is in host byte-order.
// Returns an error if the deletion did not succeed.
func (pm *PolicyMap) Delete(id uint32, dport uint16, proto u8proto.U8proto, trafficDirection trafficdirection.TrafficDirection) error {
k := newKey(id, dport, proto, trafficDirection)
return pm.Map.Delete(&k)
}
// DeleteEntry removes an entry from the PolicyMap. It can be used in
// conjunction with DumpToSlice() to inspect and delete map entries.
func (pm *PolicyMap) DeleteEntry(entry *PolicyEntryDump) error {
return pm.Map.Delete(&entry.Key)
}
// String returns a human-readable string representing the policy map.
func (pm *PolicyMap) String() string {
path, err := pm.Path()
if err != nil {
return err.Error()
}
return path
}
func (pm *PolicyMap) Dump() (string, error) {
entries, err := pm.DumpToSlice()
if err != nil {
return "", err
}
return entries.String(), nil
}
func (pm *PolicyMap) DumpToSlice() (PolicyEntriesDump, error) {
entries := PolicyEntriesDump{}
cb := func(key bpf.MapKey, value bpf.MapValue) {
eDump := PolicyEntryDump{
Key: *key.DeepCopyMapKey().(*PolicyKey),
PolicyEntry: *value.DeepCopyMapValue().(*PolicyEntry),
}
entries = append(entries, eDump)
}
err := pm.DumpWithCallback(cb)
return entries, err
}
func newMap(path string) *PolicyMap {
mapType := bpf.MapTypeHash
flags := bpf.GetPreAllocateMapFlags(mapType)
return &PolicyMap{
Map: bpf.NewMap(
path,
mapType,
&PolicyKey{},
SizeofPolicyKey,
&PolicyEntry{},
SizeofPolicyEntry,
MaxEntries,
flags, 0,
bpf.ConvertKeyValue,
),
}
}
// OpenOrCreate opens (or creates) a policy map at the specified path, which
// is used to govern which peer identities can communicate with the endpoint
// protected by this map.
func OpenOrCreate(path string) (*PolicyMap, bool, error) {
m := newMap(path)
isNewMap, err := m.OpenOrCreate()
return m, isNewMap, err
}
// Create creates a policy map at the specified path.
func Create(path string) (bool, error) {
m := newMap(path)
return m.Create()
}
// Open opens the policymap at the specified path.
func Open(path string) (*PolicyMap, error) {
m := newMap(path)
if err := m.Open(); err != nil {
return nil, err
}
return m, nil
}
// InitMapInfo updates the map info defaults for policy maps.
func InitMapInfo(maxEntries int) {
MaxEntries = maxEntries
}
// InitCallMap creates the policy call maps in the kernel.
func InitCallMaps(haveEgressCallMap bool) error {
policyCallMap := bpf.NewMap(PolicyCallMapName,
bpf.MapTypeProgArray,
&CallKey{},
int(unsafe.Sizeof(CallKey{})),
&CallValue{},
int(unsafe.Sizeof(CallValue{})),
int(PolicyCallMaxEntries),
0,
0,
bpf.ConvertKeyValue,
)
_, err := policyCallMap.Create()
if err == nil && haveEgressCallMap {
policyEgressCallMap := bpf.NewMap(PolicyEgressCallMapName,
bpf.MapTypeProgArray,
&CallKey{},
int(unsafe.Sizeof(CallKey{})),
&CallValue{},
int(unsafe.Sizeof(CallValue{})),
int(PolicyCallMaxEntries),
0,
0,
bpf.ConvertKeyValue,
)
_, err = policyEgressCallMap.Create()
}
return err
}