-
Notifications
You must be signed in to change notification settings - Fork 163
/
Copy pathinterface.go
206 lines (178 loc) · 5.69 KB
/
interface.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
// Copyright 2018 ETH Zurich
//
// 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 egress
import (
"fmt"
"math"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/common"
"github.com/scionproto/scion/go/lib/log"
"github.com/scionproto/scion/go/lib/ringbuf"
"github.com/scionproto/scion/go/lib/sciond"
"github.com/scionproto/scion/go/lib/snet"
"github.com/scionproto/scion/go/lib/spath/spathmeta"
"github.com/scionproto/scion/go/sig/mgmt"
"github.com/scionproto/scion/go/sig/siginfo"
)
func Init() {
EgressFreePkts = ringbuf.New(EgressFreePktsCap, func() interface{} {
return make(common.RawBytes, common.MaxMTU)
}, "egress", prometheus.Labels{"ringId": "freePkts", "sessId": ""})
}
const (
// FIXME(kormat): these relative sizes will fail if there are lots of egress dispatchers.
EgressFreePktsCap = 1024
EgressBufPkts = 32
SafetyInterval = 60 * time.Second
)
var EgressFreePkts *ringbuf.Ring
// Session defines a stateful context for sending traffic to a remote AS.
type Session interface {
// Logger defines common logging primitives
log.Logger
// IA returns the session's remote IA
IA() addr.IA
// ID returns the session's ID.
ID() mgmt.SessionType
// Conn returns the session's outbound snet Conn
Conn() snet.Conn
// Ring returns the session's ring buffer.
Ring() *ringbuf.Ring
// Remote returns the session's currently chosen SIG and path.
Remote() *RemoteInfo
// Cleanup shuts down the session and cleans resources.
Cleanup() error
// Healthy returns true if the session has a remote SIG and is receiving
// keepalive responses from it.
Healthy() bool
// PathPool returns the session's available pool of paths.
PathPool() PathPool
// AnnounceWorkerStopped is used to inform the session that its worker needed to shut down.
AnnounceWorkerStopped()
}
// Runner is implemented by objects that operate as goroutines.
type Runner interface {
Run()
}
// WorkerFactory build a worker for a specific session.
type WorkerFactory func(Session, log.Logger) Runner
type RemoteInfo struct {
Sig siginfo.Sig
SessPath *SessPath
}
func (r *RemoteInfo) String() string {
return fmt.Sprintf("Sig: %s Path: %s", &r.Sig, r.SessPath)
}
// PathPool is implemented by objects that maintain sets of paths. PathPools
// must be safe for concurrent use by multiple goroutines.
type PathPool interface {
// Paths returns the paths contained in the pool.
Paths() spathmeta.AppPathSet
// Destroy cleans up any resources associated with the PathPool.
Destroy() error
}
type SessionSet map[mgmt.SessionType]Session
const pathFailExpiration = 5 * time.Minute
type SessPathPool map[spathmeta.PathKey]*SessPath
// Return the most suitable path. Exclude a specific path, if possible.
func (spp SessPathPool) Get(exclude *spathmeta.PathKey) *SessPath {
var bestSessPath *SessPath
var minFail uint16 = math.MaxUint16
var bestNonExpiringSessPath *SessPath
var minNonExpiringFail uint16 = math.MaxUint16
for k, v := range spp {
if exclude != nil && k == *exclude {
continue
}
if v.failCount < minFail {
bestSessPath = v
minFail = v.failCount
}
if v.failCount < minNonExpiringFail && !v.IsCloseToExpiry() {
bestNonExpiringSessPath = v
minNonExpiringFail = v.failCount
}
}
// Return a non-expiring path with least failures.
if bestNonExpiringSessPath != nil {
return bestNonExpiringSessPath
}
// If not possible, return the best path that's close to expiry.
if bestSessPath != nil {
return bestSessPath
}
// In the worst case return the excluded path. Given that the caller asked to exclude it
// it's probably non-functional, but it's the only option we have.
if exclude == nil {
return nil
}
return spp[*exclude]
}
func (spp SessPathPool) Update(aps spathmeta.AppPathSet) {
// Remove any old entries that aren't present in the update.
for key := range spp {
if _, ok := aps[key]; !ok {
delete(spp, key)
}
}
for key, ap := range aps {
e, ok := spp[key]
if !ok {
// This is a new path, add an entry.
spp[key] = NewSessPath(key, ap.Entry)
} else {
// This path already exists, update it.
e.pathEntry = ap.Entry
}
}
}
// A SessPath contains a path and metadata related to path health.
type SessPath struct {
key spathmeta.PathKey
pathEntry *sciond.PathReplyEntry
lastFail time.Time
failCount uint16
}
func NewSessPath(key spathmeta.PathKey, pathEntry *sciond.PathReplyEntry) *SessPath {
return &SessPath{key: key, pathEntry: pathEntry, lastFail: time.Now()}
}
func (sp *SessPath) Key() spathmeta.PathKey {
return sp.key
}
func (sp *SessPath) PathEntry() *sciond.PathReplyEntry {
return sp.pathEntry
}
func (sp *SessPath) IsCloseToExpiry() bool {
return sp.PathEntry().Path.Expiry().Before(time.Now().Add(SafetyInterval))
}
func (sp *SessPath) Fail() {
sp.lastFail = time.Now()
if sp.failCount < math.MaxInt16 {
sp.failCount += 1
}
}
func (sp *SessPath) ExpireFails() {
if time.Since(sp.lastFail) > pathFailExpiration {
sp.failCount /= 2
}
}
func (sp *SessPath) String() string {
return fmt.Sprintf("Key: %s %s lastFail: %s failCount: %d", sp.key,
sp.pathEntry.Path, sp.lastFail, sp.failCount)
}
type SessionSelector interface {
ChooseSess(b common.RawBytes) Session
}