-
Notifications
You must be signed in to change notification settings - Fork 51
/
servicecache.go
95 lines (82 loc) · 2.03 KB
/
servicecache.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
package servicecache
import (
"encoding/binary"
"fmt"
"net"
"sync"
"github.com/aporeto-inc/trireme-lib/common"
"github.com/aporeto-inc/trireme-lib/utils/portspec"
)
type entry struct {
ports *portspec.PortSpec
data interface{}
}
// ServiceCache is a new service cache
type ServiceCache struct {
local map[int]map[uint32][]*entry
remote map[int]map[uint32][]*entry
sync.RWMutex
}
// NewTable creates a new table
func NewTable() *ServiceCache {
return &ServiceCache{
local: map[int]map[uint32][]*entry{},
remote: map[int]map[uint32][]*entry{},
}
}
// Add adds a service into the cache
func (s *ServiceCache) Add(e *common.Service, data interface{}, local bool) error {
s.Lock()
defer s.Unlock()
prefixes := s.remote
if local {
prefixes = s.local
}
// If addresses are nil, I only care about ports.
if len(e.Addresses) == 0 {
_, ip, _ := net.ParseCIDR("0.0.0.0/0")
e.Addresses = []*net.IPNet{ip}
}
for _, addr := range e.Addresses {
binPrefix := binary.BigEndian.Uint32(addr.IP) & binary.BigEndian.Uint32(addr.Mask)
len, _ := addr.Mask.Size()
if _, ok := prefixes[len]; !ok {
prefixes[len] = map[uint32][]*entry{}
}
if _, ok := prefixes[len][binPrefix]; !ok {
prefixes[len][binPrefix] = []*entry{}
}
for _, spec := range prefixes[len][binPrefix] {
if spec.ports.Overlaps(e.Ports) {
return fmt.Errorf("Service port overlap for a given IP not allowed")
}
}
prefixes[len][binPrefix] = append(prefixes[len][binPrefix], &entry{
ports: e.Ports,
data: data,
})
}
return nil
}
// Find searches for a matching service, given an IP and port
func (s *ServiceCache) Find(ip net.IP, port int, local bool) interface{} {
s.RLock()
defer s.RUnlock()
prefixes := s.remote
if local {
prefixes = s.local
}
for len, prefix := range prefixes {
binPrefix := binary.BigEndian.Uint32(ip) & binary.BigEndian.Uint32(net.CIDRMask(len, 32))
entries, ok := prefix[binPrefix]
if !ok {
continue
}
for _, e := range entries {
if e.ports.IsIncluded(port) {
return e.data
}
}
}
return nil
}