-
Notifications
You must be signed in to change notification settings - Fork 156
/
publishingroutingtable.go
190 lines (166 loc) · 5.12 KB
/
publishingroutingtable.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
// Copyright 2020 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 control
import (
"io"
"net"
"sync"
"github.com/google/gopacket/layers"
"github.com/scionproto/scion/pkg/addr"
)
// NewPublishingRoutingTable publishes routes from rt via the routePublisher. The methods
// of the returned object can safely be used concurrently by multiple goroutines.
func NewPublishingRoutingTable(rcs []*RoutingChain, rt RoutingTable,
routePublisher Publisher, nextHop, sourceIPv4, sourceIPv6 net.IP) RoutingTable {
var remoteSites []*remoteSite
for _, rc := range rcs {
site := &remoteSite{
prefixes: rc.Prefixes,
trafficClasses: make(map[int]PktWriter),
ia: rc.RemoteIA,
}
for _, tm := range rc.TrafficMatchers {
site.trafficClasses[tm.ID] = nil
}
remoteSites = append(remoteSites, site)
}
return &publishingRoutingTable{
routingTable: rt,
routePublisher: routePublisher,
nextHop: nextHop,
sourceIPv4: sourceIPv4,
sourceIPv6: sourceIPv6,
remoteSites: remoteSites,
}
}
type publishingRoutingTable struct {
mutex sync.RWMutex
routingTable RoutingTable
routePublisher Publisher
nextHop net.IP
sourceIPv4 net.IP
sourceIPv6 net.IP
// remoteSites is a list of remote sites. Site is a part of remote AS serving
// particular set of prefixes.
remoteSites []*remoteSite
}
type remoteSite struct {
prefixes []*net.IPNet
trafficClasses map[int]PktWriter
ia addr.IA
}
func (r *remoteSite) healthy() bool {
// This is the core of the health-determination mechanism.
// If remote site is unhealthy, the corresponding routes will be retracted.
// Note that there's no good solution here. Either we consider remote site
// unhealthy if all traffic classes can't get through or if one of the traffic
// classes can't. In the former case (the currently used solution) a single
// healthy traffic class overrides all the unhealthy ones and the unhealthy
// traffic classes get blackholed. In the latter case a single unhealthy traffic
// class results in retraction of prefixes and even healhy traffic classes may
// stop working (depending on whether user's backup solution works or not).
for _, session := range r.trafficClasses {
if session != nil {
return true
}
}
return false
}
func (rtw *publishingRoutingTable) RouteIPv4(pkt layers.IPv4) PktWriter {
rtw.mutex.RLock()
defer rtw.mutex.RUnlock()
return rtw.routingTable.RouteIPv4(pkt)
}
func (rtw *publishingRoutingTable) RouteIPv6(pkt layers.IPv6) PktWriter {
rtw.mutex.RLock()
defer rtw.mutex.RUnlock()
return rtw.routingTable.RouteIPv6(pkt)
}
func (rtw *publishingRoutingTable) SetSession(index int, session PktWriter) error {
rtw.mutex.Lock()
defer rtw.mutex.Unlock()
return rtw.setSessionLocked(index, session)
}
func (rtw *publishingRoutingTable) Close() error {
rtw.mutex.Lock()
defer rtw.mutex.Unlock()
rtw.routePublisher.Close()
rtw.routePublisher = nil
return rtw.routingTable.Close()
}
func (rtw *publishingRoutingTable) setSessionLocked(index int, session PktWriter) error {
if err := rtw.routingTable.SetSession(index, session); err != nil {
return err
}
for _, site := range rtw.remoteSites {
_, ok := site.trafficClasses[index]
if !ok {
continue
}
wasHealthy := site.healthy()
site.trafficClasses[index] = session
isHealthy := site.healthy()
if !wasHealthy && isHealthy {
for _, prefix := range site.prefixes {
rtw.routePublisher.AddRoute(Route{
Prefix: prefix,
Source: rtw.sourceForPrefix(prefix),
NextHop: rtw.nextHop,
IA: site.ia,
})
}
}
}
return nil
}
func (rtw *publishingRoutingTable) ClearSession(index int) error {
rtw.mutex.Lock()
defer rtw.mutex.Unlock()
if err := rtw.routingTable.ClearSession(index); err != nil {
return err
}
for _, site := range rtw.remoteSites {
_, ok := site.trafficClasses[index]
if !ok {
continue
}
wasHealthy := site.healthy()
site.trafficClasses[index] = nil
isHealthy := site.healthy()
if wasHealthy && !isHealthy {
for _, prefix := range site.prefixes {
rtw.routePublisher.DeleteRoute(Route{
Prefix: prefix,
Source: rtw.sourceForPrefix(prefix),
NextHop: rtw.nextHop,
IA: site.ia,
})
}
}
}
return nil
}
func (rtw *publishingRoutingTable) DiagnosticsWrite(w io.Writer) {
if dw, ok := rtw.routingTable.(DiagnosticsWriter); ok {
dw.DiagnosticsWrite(w)
}
}
// sourceForPrefix returns the appropriate source hint for IPv4/IPv6 prefixes
func (rtw *publishingRoutingTable) sourceForPrefix(prefix *net.IPNet) net.IP {
if prefix.IP.To4() == nil {
return rtw.sourceIPv6
} else {
return rtw.sourceIPv4
}
}