-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathRouteviews.go
318 lines (273 loc) · 10.4 KB
/
Routeviews.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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
package main
import (
"fmt"
"github.com/osrg/gobgp/pkg/packet/bgp"
"net"
"github.com/osrg/gobgp/pkg/packet/mrt"
"log"
"time"
)
var bgpd BGPDump
type BGPDump struct {
Date time.Time
}
func (b *BGPDump) parseRIBAndInsert(ribFileName string) error {
scanner, err := getRightScanner(ribFileName)
if err != nil {
return err
}
scanner.Split(mrt.SplitMrt)
countSplitsInRIB := 0
countMrtRibs := 0
countSingleEntries := 0
indexTableCount := 0
entries:
for scanner.Scan() {
countSplitsInRIB++
if countSplitsInRIB > 10000000 { //for debugging. Vary threshold to preferred amount of read RIB entries
fmt.Println("Too many entries. STOPPING....")
break
}
var m message
data := scanner.Bytes()
hdr := &mrt.MRTHeader{}
errh := hdr.DecodeFromBytes(data[:mrt.MRT_COMMON_HEADER_LEN])
if errh != nil { //changed to errh (before there stood err (probably a mistake)
return errh
}
msg, err := mrt.ParseMRTBody(hdr, data[mrt.MRT_COMMON_HEADER_LEN:])
if err != nil {
log.Printf("could not parse mrt body: %v", err)
continue entries
}
if msg.Header.Type != mrt.TABLE_DUMPv2 {
return fmt.Errorf("unexpected message type: %d", msg.Header.Type)
}
//m.timestamp = msg.Header.Timestamp
m.isAnnouncement = true //RIBS are all announcements
//m.bgpsubtype = msg.Header.SubType
switch mtrBody := msg.Body.(type) {
case *mrt.PeerIndexTable:
indexTableCount++
peers = msg.Body.(*mrt.PeerIndexTable).Peers //added
if indexTableCount != 1 {
return fmt.Errorf("got > 1 PeerIndexTable")
}
insertPeers()
case *mrt.Rib:
countMrtRibs++
rib := msg.Body.(*mrt.Rib)
prefix := rib.Prefix
_, ipnet, err := net.ParseCIDR(prefix.String())
if err != nil {
fmt.Println(Red("Could not parse to subnet", err))
}
m.subnet = *ipnet
if m.subnet.IP.To4() != nil {
m.subnetAsBits = convertIPtoBits(m.subnet)
}
if len(rib.Entries) < 0 {
return fmt.Errorf("no entries")
}
for i := 0; i < len(rib.Entries); i++ {
countSingleEntries++
m.timestamp = uint32(rib.Entries[i].OriginatedTime)
m.peerID = ourPeers[rib.Entries[i].PeerIndex].id //during the initialization PeerIndex should already be equal to id
setASpathInMessage(&m, rib.Entries[i].PathAttributes)
insertAndFindConflicts(m, findConflictsInRib)
}
default:
return fmt.Errorf("unsupported message %v %s", mtrBody)
}
}
fmt.Println(Teal("items in RIB file, seperated by MRT splitting function: ", countSplitsInRIB))
fmt.Println(Teal("of these items, how many were of type mrt.Ribs: ", countMrtRibs))
fmt.Println(Teal("all together there were how many single RIB entries: ", countSingleEntries))
fmt.Println(Teal("found peer index tables: ", indexTableCount))
return nil
}
var messages BGPDump
func (b *BGPDump) parseUpdatesAndInsert(inputFile string, findConflicts bool) error {
scanner, err := getRightScanner(inputFile)
if err != nil {
return err
}
scanner.Split(mrt.SplitMrt)
//some non-essential logging variables to keep overview
countEntries := 0
countNotRelevantMRTBody := 0
countNotRelevantBGPMsgBody := 0
countNeitherUpdateNorWithdrawl := 0
// countConflictTriggers := 0
entries:
for scanner.Scan() {
if countEntries > 100000000 { //for debugging. vary threshold to desired limit
fmt.Println(Red("Too many entries in updates. STOPPING..."))
break
}
countEntries++
//extracting the bytes for the next MRT entry
data := scanner.Bytes()
//extracting the MRT header from the bytes
hdr := &mrt.MRTHeader{}
errh := hdr.DecodeFromBytes(data[:mrt.MRT_COMMON_HEADER_LEN])
if errh != nil {
return errh
}
/*
The BGP4MP_ET type is not implemented in the mrt-Module.
The difference to BGP4MP is only a more precise timestamp (containting microseconds) in an extra field.
To be able to use the unmodified mrt-Module, we cast a BGP4MP_ET message to a BGP4MP message and "jump over" the
extra field containing the microseconds. The normal timestamp is of course parsed as normal.
*/
var skip uint32
skip = 0
if hdr.Type == mrt.BGP4MP_ET {
skip = 4 //we will later "jump over" the 4 bytes field containing the microseconds
hdr.Type = mrt.BGP4MP //we change the type indicator from BGP4MP_ET to BGP4MP
}
//extracting the MRT Message. The MRT Header is already extracted. The MRT Body is extracted in the following. Both are stored in msg
var msg *mrt.MRTMessage
var err error
hdr.Len = hdr.Len - skip
msg, err = mrt.ParseMRTBody(hdr, data[mrt.MRT_COMMON_HEADER_LEN+skip:])
if err != nil {
log.Printf("could not parse mrt body: %v", err)
continue entries
}
switch msg.Body.(type) {
case *mrt.BGP4MPMessage: //we expect the type of the MRT Body to be of type BGP4MPMessage
bgp4msg := msg.Body.(*mrt.BGP4MPMessage)
//A BGP4MPMessage contains itself a BGPMessage consisting of a Body and a header
bgpmsg := bgp4msg.BGPMessage
switch bgpmsg.Body.(type) {
case *bgp.BGPUpdate: //we expect the body to be of type BGPUpdate
bgpmsgBody := bgpmsg.Body.(*bgp.BGPUpdate)
//we start with the creation of a new instance of our Message type with the already extracted attributes
var m message
m.timestamp = msg.Header.Timestamp
//m.bgpsubtype = msg.Header.SubType
peerip := bgp4msg.PeerIpAddress.String()
peerAs := bgp4msg.PeerAS
m.peerID = findPeerIDbyIP(peerip, peerAs)
//we make sure that we either have a BGP announcement or withdrawal
if len(bgpmsgBody.NLRI) == 0 && len(bgpmsgBody.WithdrawnRoutes) == 0 {
countNeitherUpdateNorWithdrawl++
fmt.Println(Red("BGP Message seems to be neither Announcement nor Withdrawal: ", bgpmsgBody))
continue entries
}
subnetsAnouncments := bgpmsgBody.NLRI //if it is an announcement we extract the announced prefixes from NLRI and store them in subnets
subnetsWithdrawls := bgpmsgBody.WithdrawnRoutes //if it is a withdrawal we extract the withdrawn prefixes from WithdrawnRoutes and store them in subnets
for i := 0; i < len(subnetsAnouncments)+len(subnetsWithdrawls); i++ { //for each announced or withdrawn subnet we create a single instance of type message and insert it into our trie
var ipnet *net.IPNet
var err error
if i < len(subnetsAnouncments) {
m.isAnnouncement = true
_, ipnet, err = net.ParseCIDR(subnetsAnouncments[i].String())
} else {
m.isAnnouncement = false
_, ipnet, err = net.ParseCIDR(subnetsWithdrawls[i-len(subnetsAnouncments)].String())
}
if err != nil {
fmt.Println(Red("Could not parse to subnet", err))
}
m.subnet = *ipnet
if m.subnet.IP.To4() != nil {
m.subnetAsBits = convertIPtoBits(m.subnet)
}
//if we have an Announcement we also have to set the AS path (and AS4path) attributes
if m.isAnnouncement {
setASpathInMessage(&m, bgpmsgBody.PathAttributes)
}
//we insert the current message in the IPv4 or IPv6 trie
insertAndFindConflicts(m, findConflicts)
}
default:
countNotRelevantBGPMsgBody++
}
default:
countNotRelevantMRTBody++
fmt.Println(Red("MRT Body is not of type BGP4MPMessage, but of: ", msg.Header.Type))
}
//fmt.Println(msg)
//fmt.Println()
}
fmt.Println(Teal("read update entries: ", countEntries))
fmt.Println(Teal("not parsable MRT body (not of type BGP4MPMessage): ", countNotRelevantMRTBody))
fmt.Println(Teal("not parsable BGP message body (not of type BGPUpdate): ", countNotRelevantBGPMsgBody))
fmt.Println(Teal("BGP update messages with neither announcements nor withdrawls:", countNeitherUpdateNorWithdrawl))
return nil
}
func setASpathInMessage(m *message, attributes []bgp.PathAttributeInterface) { //this function sets the aspath in a message m
//In case there are multiple AS paths, we want to choose one of the smallest AS paths
minimumLengthOfBestASPath := 10000
var bestASPath []uint32
minimumLengthOfBestRealASPath := 10000
var bestRealASPath []uint32
attrs:
// we iterate over the attributes
for i := 0; i < len(attributes); i++ {
switch pa := attributes[i].(type) {
case *bgp.PathAttributeAsPath: // we have an AS path
if len(pa.Value) < 1 {
continue attrs
}
if len(pa.Value) > 1 {
}
if v, ok := pa.Value[0].(*bgp.As4PathParam); ok { // we expect the AS paths to be of kind "AS4PathParam"
if len(v.AS) < 1 { //reminder: there stood 0. this was probably wrong
continue attrs
}
if len(v.AS) < minimumLengthOfBestASPath { // we compare the path length to our previous best path length
minimumLengthOfBestASPath = len(v.AS)
bestASPath = v.AS
}
}
if v, ok := pa.Value[0].(*bgp.AsPathParam); ok { // deprecated but still in use
if len(v.AS) < 1 { //reminder there stood 0. this was probably wrong
continue attrs
}
if len(v.AS) < minimumLengthOfBestASPath {
minimumLengthOfBestASPath = len(v.AS)
//we have to cast each uint16 in the slice into an uint32
bestASPath = make([]uint32, len(v.AS))
for i, v2 := range v.AS {
bestASPath[i] = uint32(v2)
}
}
}
case *bgp.PathAttributeAs4Path:
/*
some old peers do not support 4 byte long AS numbers. To be able to communicate with them, the well known AS number
23456 was introduced. When this AS number appears in an AS path, we also have the AS4path attribute. In this attribute
we have the "real" AS path (with 4 byte long AS numbers) stored.
*/
if len(pa.Value) < 1 {
continue attrs
}
pv := pa.Value[0]
if len(pv.AS) < 1 {
continue attrs
}
if len(pv.AS) < minimumLengthOfBestRealASPath {
minimumLengthOfBestRealASPath = len(pv.AS)
bestRealASPath = pv.AS //hint: if we get problems here, maybe pv.AS is not of type AS4PathParam but ASPathParam
}
default:
// fmt.Println(" no PathAttributeAS(4)Path but ", pa)
}
}
m.aspath = bestASPath // we set the aspath attribute in message m
if bestASPath != nil {
m.origin = bestASPath[len(bestASPath)-1]
} else {
fmt.Println("no best AS path specified. Could not set origin AS")
}
if minimumLengthOfBestRealASPath != 10000 { // if needed, we set the aspath to the "real" ASPath
realAsPath := make([]uint32, len(bestRealASPath))
copy(realAsPath, bestRealASPath)
realAsPath = append([]uint32{m.aspath[0]}, realAsPath...) // it seems that the as4path does not include the first AS number. We want to see the full path and prepend this AS number
m.aspath = realAsPath
m.origin = realAsPath[len(realAsPath)-1]
}
}