blob: 51cbc332f609ba6d547d9305a04ad5c8162d4629 [file] [log] [blame]
Suharsh Sivakumar720b7042014-12-22 17:33:23 -08001package ipc
2
3import (
4 "fmt"
5 "reflect"
6 "sync"
7
Jiri Simsa764efb72014-12-25 20:57:03 -08008 "v.io/core/veyron2/ipc"
9 "v.io/core/veyron2/ipc/stream"
10 "v.io/core/veyron2/security"
Suharsh Sivakumar720b7042014-12-22 17:33:23 -080011)
12
13// clientEncodeBlessings gets or inserts the blessings into the cache.
14func clientEncodeBlessings(cache stream.VCDataCache, blessings security.Blessings) ipc.BlessingsRequest {
15 blessingsCacheAny := cache.GetOrInsert(clientBlessingsKey{}, newClientBlessingsCache)
16 blessingsCache := blessingsCacheAny.(*clientBlessingsCache)
17 return blessingsCache.getOrInsert(blessings)
18}
19
20// clientAckBlessings verifies that the server has updated its cache to include blessings.
21// This means that subsequent rpcs from the client with blessings can send only a cache key.
22func clientAckBlessings(cache stream.VCDataCache, blessings security.Blessings) {
23 blessingsCacheAny := cache.GetOrInsert(clientBlessingsKey{}, newClientBlessingsCache)
24 blessingsCache := blessingsCacheAny.(*clientBlessingsCache)
25 blessingsCache.acknowledge(blessings)
26}
27
28// serverDecodeBlessings insert the key and blessings into the cache or get the blessings if only
29// key is provided in req.
30func serverDecodeBlessings(cache stream.VCDataCache, req ipc.BlessingsRequest, stats *ipcStats) (security.Blessings, error) {
31 blessingsCacheAny := cache.GetOrInsert(serverBlessingsKey{}, newServerBlessingsCache)
32 blessingsCache := blessingsCacheAny.(*serverBlessingsCache)
33 return blessingsCache.getOrInsert(req, stats)
34}
35
36// IMPLEMENTATION DETAILS BELOW
37
38// clientBlessingsCache is a thread-safe map from blessings to cache key.
39type clientBlessingsCache struct {
40 sync.RWMutex
41 m map[security.Blessings]clientCacheValue
42 key uint64
43}
44
45type clientCacheValue struct {
46 key uint64
47 // ack is set to true once the server has confirmed receipt of the cache key.
48 // Clients that insert into the cache when ack is false must send both the key
49 // and the blessings.
50 ack bool
51}
52
53// clientBlessingsKey is the key used to retrieve the clientBlessingsCache from the VCDataCache.
54type clientBlessingsKey struct{}
55
56func newClientBlessingsCache() interface{} {
57 return &clientBlessingsCache{m: make(map[security.Blessings]clientCacheValue)}
58}
59
60func (c *clientBlessingsCache) getOrInsert(blessings security.Blessings) ipc.BlessingsRequest {
61 c.RLock()
62 val, exists := c.m[blessings]
63 c.RUnlock()
64 if exists {
65 return c.makeBlessingsRequest(val, blessings)
66 }
67 // if the val doesn't exist we must create a new key, update the cache, and send the key and blessings.
68 c.Lock()
69 defer c.Unlock()
70 // we must check that the val wasn't inserted in the time we changed locks.
71 val, exists = c.m[blessings]
72 if exists {
73 return c.makeBlessingsRequest(val, blessings)
74 }
75 newVal := clientCacheValue{key: c.nextKeyLocked()}
76 c.m[blessings] = newVal
77 return c.makeBlessingsRequest(newVal, blessings)
78}
79
80func (c *clientBlessingsCache) acknowledge(blessings security.Blessings) {
81 c.Lock()
82 val := c.m[blessings]
83 val.ack = true
84 c.m[blessings] = val
85 c.Unlock()
86}
87
88func (c *clientBlessingsCache) makeBlessingsRequest(val clientCacheValue, blessings security.Blessings) ipc.BlessingsRequest {
89 if val.ack {
90 // when the value is acknowledged, only send the key, since the server has confirmed that it knows the key.
91 return ipc.BlessingsRequest{Key: val.key}
92 }
93 // otherwise we still need to send both key and blessings, but we must ensure that we send the same key.
94 wireBlessings := security.MarshalBlessings(blessings)
95 return ipc.BlessingsRequest{val.key, &wireBlessings}
96}
97
98// nextKeyLocked creates a new key for inserting blessings. It must be called after acquiring a writer lock.
99func (c *clientBlessingsCache) nextKeyLocked() uint64 {
100 c.key++
101 return c.key
102}
103
104// serverBlessingsCache is a thread-safe map from cache key to blessings.
105type serverBlessingsCache struct {
106 sync.RWMutex
107 m map[uint64]security.Blessings
108}
109
110// serverBlessingsKey is the key used to retrieve the serverBlessingsCache from the VCDataCache.
111type serverBlessingsKey struct{}
112
113func newServerBlessingsCache() interface{} {
114 return &serverBlessingsCache{m: make(map[uint64]security.Blessings)}
115}
116
117func (c *serverBlessingsCache) getOrInsert(req ipc.BlessingsRequest, stats *ipcStats) (security.Blessings, error) {
118 // In the case that the key sent is 0, we are running in VCSecurityNone and should
119 // return nil for the client Blessings.
120 if req.Key == 0 {
121 return nil, nil
122 }
123 if req.Blessings == nil {
124 // Fastpath, lookup based on the key.
125 c.RLock()
126 cached, exists := c.m[req.Key]
127 c.RUnlock()
128 if !exists {
129 return nil, fmt.Errorf("ipc: key was not in the cache")
130 }
131 stats.recordBlessingCache(true)
132 return cached, nil
133 }
Suharsh Sivakumardefea2b2015-01-13 09:33:50 -0800134 // Always count the slow path as a cache miss since we do not get the benefit of sending only the cache key.
135 stats.recordBlessingCache(false)
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800136 // Slowpath, might need to update the cache, or check that the received blessings are
137 // the same as what's in the cache.
138 recv, err := security.NewBlessings(*req.Blessings)
139 if err != nil {
140 return nil, fmt.Errorf("ipc: create new client blessings failed: %v", err)
141 }
142 c.Lock()
143 defer c.Unlock()
144 if cached, exists := c.m[req.Key]; exists {
145 // TODO(suharshs): Replace this reflect.DeepEqual() with a less expensive check.
146 if !reflect.DeepEqual(cached, recv) {
147 return nil, fmt.Errorf("client sent invalid Blessings")
148 }
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800149 return cached, nil
150 }
151 c.m[req.Key] = recv
Suharsh Sivakumar720b7042014-12-22 17:33:23 -0800152 return recv, nil
153}