-
Notifications
You must be signed in to change notification settings - Fork 163
/
Copy pathifstate.go
260 lines (233 loc) · 7.42 KB
/
ifstate.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
// Copyright 2019 Anapaya Systems
//
// 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 ifstate
import (
"sync"
"time"
"github.com/scionproto/scion/go/lib/common"
"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
"github.com/scionproto/scion/go/lib/topology"
)
const (
// DefaultKeepaliveInterval is the default time between sending IFID
// keepalive packets to the neighbor.
DefaultKeepaliveInterval = time.Second
// DefaultKeepaliveTimeout specifies the default for how long an interface
// can receive no IFID keepalive packets until it is considered expired.
DefaultKeepaliveTimeout = 3 * DefaultKeepaliveInterval
)
const (
// Inactive indicates that the interface has not been activated or
// expired yet.
Inactive State = "Inactive"
// Active indicates that the interface is active.
Active State = "Active"
// Expired indicates that the interface is expired.
Expired State = "Expired"
// Revoked indicates that the interface is revoked.
Revoked State = "Revoked"
)
// State is the state of an interface.
type State string
// Config enables configuration of the interfaces.
type Config struct {
// KeepaliveTimeout specifies for how long an interface can receive no
// IFID keepalive packets until it is considered expired.
KeepaliveTimeout time.Duration
}
// InitDefaults initializes the config fields that are not set to the
// default values.
func (c *Config) InitDefaults() {
if c.KeepaliveTimeout == 0 {
c.KeepaliveTimeout = DefaultKeepaliveTimeout
}
}
// Interfaces keeps track of all interfaces of the AS.
type Interfaces struct {
mu sync.RWMutex
intfs map[common.IFIDType]*Interface
cfg Config
}
// NewInterfaces initializes the the interfaces with the provided interface info map.
func NewInterfaces(ifInfomap topology.IfInfoMap, cfg Config) *Interfaces {
intfs := &Interfaces{
cfg: cfg,
}
intfs.cfg.InitDefaults()
intfs.Update(ifInfomap)
return intfs
}
// Update updates the interface mapping. Interfaces no longer present in
// the topology are removed. The state of existing interfaces is preserved.
// New interfaces are added as inactive.
func (intfs *Interfaces) Update(ifInfomap topology.IfInfoMap) {
intfs.mu.Lock()
defer intfs.mu.Unlock()
m := make(map[common.IFIDType]*Interface, len(intfs.intfs))
for ifid, info := range ifInfomap {
if intf, ok := intfs.intfs[ifid]; ok {
intf.updateTopoInfo(info)
m[ifid] = intf
} else {
m[ifid] = &Interface{
topoInfo: info,
state: Inactive,
lastActivate: time.Now(),
cfg: intfs.cfg,
}
}
}
intfs.intfs = m
}
// Reset resets all interface states to inactive. This should be called
// by the beacon server if it is elected leader.
func (intfs *Interfaces) Reset() {
intfs.mu.RLock()
defer intfs.mu.RUnlock()
for _, intf := range intfs.intfs {
intf.reset()
}
}
// All returns a copy of the map from interface id to interface.
func (intfs *Interfaces) All() map[common.IFIDType]*Interface {
intfs.mu.RLock()
defer intfs.mu.RUnlock()
res := make(map[common.IFIDType]*Interface, len(intfs.intfs))
for ifid, intf := range intfs.intfs {
res[ifid] = intf
}
return res
}
// Get returns the interface for the specified id, or nil if not present.
func (intfs *Interfaces) Get(ifid common.IFIDType) *Interface {
intfs.mu.RLock()
defer intfs.mu.RUnlock()
return intfs.intfs[ifid]
}
// Interface keeps track of the interface state.
type Interface struct {
mu sync.RWMutex
topoInfo topology.IFInfo
state State
revocation *path_mgmt.SignedRevInfo
lastOriginate time.Time
lastPropagate time.Time
lastActivate time.Time
cfg Config
}
// Activate activates the interface the keep alive is received from when
// necessary, and sets the remote interface id. The return value indicates
// the previous state of the interface.
func (intf *Interface) Activate(remote common.IFIDType) State {
intf.mu.Lock()
defer intf.mu.Unlock()
prev := intf.state
intf.state = Active
intf.lastActivate = time.Now()
intf.topoInfo.RemoteIFID = remote
intf.revocation = nil
return prev
}
// Expire checks whether the interface has not been activated for a certain
// amount of time. If that is the case and the current state is inactive or
// active, the state changes to Expired. The times for last beacon origination
// and propagation are reset to the zero value. The return value indicates,
// whether the state is expired or revoked when the call returns.
func (intf *Interface) Expire() bool {
intf.mu.Lock()
defer intf.mu.Unlock()
if intf.state == Expired || intf.state == Revoked {
return true
}
if time.Now().Sub(intf.lastActivate) > intf.cfg.KeepaliveTimeout {
intf.lastOriginate = time.Time{}
intf.lastPropagate = time.Time{}
intf.state = Expired
return true
}
return false
}
// Revoke changes the state of the interface to revoked and updates the
// revocation, unless the current state is active. In that case, the
// interface has been activated in the meantime and should not be revoked.
// This is indicated through an error.
func (intf *Interface) Revoke(rev *path_mgmt.SignedRevInfo) error {
intf.mu.Lock()
defer intf.mu.Unlock()
if intf.state == Active {
return common.NewBasicError("Interface activated in the meantime", nil)
}
intf.state = Revoked
intf.revocation = rev
return nil
}
// Revocation returns the revocation.
func (intf *Interface) Revocation() *path_mgmt.SignedRevInfo {
intf.mu.RLock()
defer intf.mu.RUnlock()
return intf.revocation
}
// TopoInfo returns the topology information.
func (intf *Interface) TopoInfo() topology.IFInfo {
intf.mu.RLock()
defer intf.mu.RUnlock()
return intf.topoInfo
}
// State returns the current state of the interface.
func (intf *Interface) State() State {
intf.mu.RLock()
defer intf.mu.RUnlock()
return intf.state
}
// Originate sets the time this interface has been originated on last.
func (intf *Interface) Originate(now time.Time) {
intf.mu.RLock()
intf.lastOriginate = now
intf.mu.RUnlock()
}
// LastOriginate indicates the last time this interface has been originated on.
func (intf *Interface) LastOriginate() time.Time {
intf.mu.RLock()
defer intf.mu.RUnlock()
return intf.lastOriginate
}
// Propagate sets the time this interface has been propagated on last.
func (intf *Interface) Propagate(now time.Time) {
intf.mu.RLock()
intf.lastPropagate = now
intf.mu.RUnlock()
}
// LastPropagate indicates the last time this interface has been propagated on.
func (intf *Interface) LastPropagate() time.Time {
intf.mu.RLock()
defer intf.mu.RUnlock()
return intf.lastPropagate
}
func (intf *Interface) reset() {
intf.mu.Lock()
defer intf.mu.Unlock()
intf.state = Inactive
intf.revocation = nil
intf.lastOriginate = time.Time{}
intf.lastPropagate = time.Time{}
// Set the starting point for the timeout interval.
intf.lastActivate = time.Now()
}
func (intf *Interface) updateTopoInfo(topoInfo topology.IFInfo) {
intf.mu.Lock()
defer intf.mu.Unlock()
// Keep remote topo info.
topoInfo.RemoteIFID = intf.topoInfo.RemoteIFID
intf.topoInfo = topoInfo
}