blob: 43030fb6a557d3a37b9b40bb257bc28aac7e3f6d [file] [log] [blame]
Holger Hildebrandtfa074992020-03-27 15:42:06 +00001// Copyright 2017 Google, Inc. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the LICENSE file in the root of the source
5// tree.
6//
7
8package layers
9
10import (
11 "encoding/binary"
12 "errors"
13
14 "github.com/google/gopacket"
15)
16
17// BFD Control Packet Format
18// -------------------------
19// The current version of BFD's RFC (RFC 5880) contains the following
20// diagram for the BFD Control packet format:
21//
22// 0 1 2 3
23// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
24// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25// |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length |
26// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27// | My Discriminator |
28// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29// | Your Discriminator |
30// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31// | Desired Min TX Interval |
32// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33// | Required Min RX Interval |
34// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35// | Required Min Echo RX Interval |
36// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37//
38// An optional Authentication Section MAY be present:
39//
40// 0 1 2 3
41// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
42// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43// | Auth Type | Auth Len | Authentication Data... |
44// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45//
46//
47// Simple Password Authentication Section Format
48// ---------------------------------------------
49// 0 1 2 3
50// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
51// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52// | Auth Type | Auth Len | Auth Key ID | Password... |
53// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54// | ... |
55// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56//
57//
58// Keyed MD5 and Meticulous Keyed MD5 Authentication Section Format
59// ----------------------------------------------------------------
60// 0 1 2 3
61// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
62// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63// | Auth Type | Auth Len | Auth Key ID | Reserved |
64// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65// | Sequence Number |
66// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
67// | Auth Key/Digest... |
68// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
69// | ... |
70// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71//
72//
73// Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section Format
74// ------------------------------------------------------------------
75// 0 1 2 3
76// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
77// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
78// | Auth Type | Auth Len | Auth Key ID | Reserved |
79// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80// | Sequence Number |
81// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82// | Auth Key/Hash... |
83// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84// | ... |
85// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86//
87// From https://tools.ietf.org/rfc/rfc5880.txt
88const bfdMinimumRecordSizeInBytes int = 24
89
90// BFDVersion represents the version as decoded from the BFD control message
91type BFDVersion uint8
92
93// BFDDiagnostic represents diagnostic infomation about a BFD session
94type BFDDiagnostic uint8
95
96// constants that define BFDDiagnostic flags
97const (
98 BFDDiagnosticNone BFDDiagnostic = 0 // No Diagnostic
99 BFDDiagnosticTimeExpired BFDDiagnostic = 1 // Control Detection Time Expired
100 BFDDiagnosticEchoFailed BFDDiagnostic = 2 // Echo Function Failed
101 BFDDiagnosticNeighborSignalDown BFDDiagnostic = 3 // Neighbor Signaled Session Down
102 BFDDiagnosticForwardPlaneReset BFDDiagnostic = 4 // Forwarding Plane Reset
103 BFDDiagnosticPathDown BFDDiagnostic = 5 // Path Down
104 BFDDiagnosticConcatPathDown BFDDiagnostic = 6 // Concatenated Path Down
105 BFDDiagnosticAdminDown BFDDiagnostic = 7 // Administratively Down
106 BFDDiagnosticRevConcatPathDown BFDDiagnostic = 8 // Reverse Concatenated Path Dow
107)
108
109// String returns a string version of BFDDiagnostic
110func (bd BFDDiagnostic) String() string {
111 switch bd {
112 default:
113 return "Unknown"
114 case BFDDiagnosticNone:
115 return "None"
116 case BFDDiagnosticTimeExpired:
117 return "Control Detection Time Expired"
118 case BFDDiagnosticEchoFailed:
119 return "Echo Function Failed"
120 case BFDDiagnosticNeighborSignalDown:
121 return "Neighbor Signaled Session Down"
122 case BFDDiagnosticForwardPlaneReset:
123 return "Forwarding Plane Reset"
124 case BFDDiagnosticPathDown:
125 return "Path Down"
126 case BFDDiagnosticConcatPathDown:
127 return "Concatenated Path Down"
128 case BFDDiagnosticAdminDown:
129 return "Administratively Down"
130 case BFDDiagnosticRevConcatPathDown:
131 return "Reverse Concatenated Path Down"
132 }
133}
134
135// BFDState represents the state of a BFD session
136type BFDState uint8
137
138// constants that define BFDState
139const (
140 BFDStateAdminDown BFDState = 0
141 BFDStateDown BFDState = 1
142 BFDStateInit BFDState = 2
143 BFDStateUp BFDState = 3
144)
145
146// String returns a string version of BFDState
147func (s BFDState) String() string {
148 switch s {
149 default:
150 return "Unknown"
151 case BFDStateAdminDown:
152 return "Admin Down"
153 case BFDStateDown:
154 return "Down"
155 case BFDStateInit:
156 return "Init"
157 case BFDStateUp:
158 return "Up"
159 }
160}
161
162// BFDDetectMultiplier represents the negotiated transmit interval,
163// multiplied by this value, provides the Detection Time for the
164// receiving system in Asynchronous mode.
165type BFDDetectMultiplier uint8
166
167// BFDDiscriminator is a unique, nonzero discriminator value used
168// to demultiplex multiple BFD sessions between the same pair of systems.
169type BFDDiscriminator uint32
170
171// BFDTimeInterval represents a time interval in microseconds
172type BFDTimeInterval uint32
173
174// BFDAuthType represents the authentication used in the BFD session
175type BFDAuthType uint8
176
177// constants that define the BFDAuthType
178const (
179 BFDAuthTypeNone BFDAuthType = 0 // No Auth
180 BFDAuthTypePassword BFDAuthType = 1 // Simple Password
181 BFDAuthTypeKeyedMD5 BFDAuthType = 2 // Keyed MD5
182 BFDAuthTypeMeticulousKeyedMD5 BFDAuthType = 3 // Meticulous Keyed MD5
183 BFDAuthTypeKeyedSHA1 BFDAuthType = 4 // Keyed SHA1
184 BFDAuthTypeMeticulousKeyedSHA1 BFDAuthType = 5 // Meticulous Keyed SHA1
185)
186
187// String returns a string version of BFDAuthType
188func (at BFDAuthType) String() string {
189 switch at {
190 default:
191 return "Unknown"
192 case BFDAuthTypeNone:
193 return "No Authentication"
194 case BFDAuthTypePassword:
195 return "Simple Password"
196 case BFDAuthTypeKeyedMD5:
197 return "Keyed MD5"
198 case BFDAuthTypeMeticulousKeyedMD5:
199 return "Meticulous Keyed MD5"
200 case BFDAuthTypeKeyedSHA1:
201 return "Keyed SHA1"
202 case BFDAuthTypeMeticulousKeyedSHA1:
203 return "Meticulous Keyed SHA1"
204 }
205}
206
207// BFDAuthKeyID represents the authentication key ID in use for
208// this packet. This allows multiple keys to be active simultaneously.
209type BFDAuthKeyID uint8
210
211// BFDAuthSequenceNumber represents the sequence number for this packet.
212// For Keyed Authentication, this value is incremented occasionally. For
213// Meticulous Keyed Authentication, this value is incremented for each
214// successive packet transmitted for a session. This provides protection
215// against replay attacks.
216type BFDAuthSequenceNumber uint32
217
218// BFDAuthData represents the authentication key or digest
219type BFDAuthData []byte
220
221// BFDAuthHeader represents authentication data used in the BFD session
222type BFDAuthHeader struct {
223 AuthType BFDAuthType
224 KeyID BFDAuthKeyID
225 SequenceNumber BFDAuthSequenceNumber
226 Data BFDAuthData
227}
228
229// Length returns the data length of the BFDAuthHeader based on the
230// authentication type
231func (h *BFDAuthHeader) Length() int {
232 switch h.AuthType {
233 case BFDAuthTypePassword:
234 return 3 + len(h.Data)
235 case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
236 return 8 + len(h.Data)
237 case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
238 return 8 + len(h.Data)
239 default:
240 return 0
241 }
242}
243
244// BFD represents a BFD control message packet whose payload contains
245// the control information required to for a BFD session.
246//
247// References
248// ----------
249//
250// Wikipedia's BFD entry:
251// https://en.wikipedia.org/wiki/Bidirectional_Forwarding_Detection
252// This is the best place to get an overview of BFD.
253//
254// RFC 5880 "Bidirectional Forwarding Detection (BFD)" (2010)
255// https://tools.ietf.org/html/rfc5880
256// This is the original BFD specification.
257//
258// RFC 5881 "Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop)" (2010)
259// https://tools.ietf.org/html/rfc5881
260// Describes the use of the Bidirectional Forwarding Detection (BFD)
261// protocol over IPv4 and IPv6 for single IP hops.
262type BFD struct {
263 BaseLayer // Stores the packet bytes and payload bytes.
264
265 Version BFDVersion // Version of the BFD protocol.
266 Diagnostic BFDDiagnostic // Diagnostic code for last state change
267 State BFDState // Current state
268 Poll bool // Requesting verification
269 Final bool // Responding to a received BFD Control packet that had the Poll (P) bit set.
270 ControlPlaneIndependent bool // BFD implementation does not share fate with its control plane
271 AuthPresent bool // Authentication Section is present and the session is to be authenticated
272 Demand bool // Demand mode is active
273 Multipoint bool // For future point-to-multipoint extensions. Must always be zero
274 DetectMultiplier BFDDetectMultiplier // Detection time multiplier
275 MyDiscriminator BFDDiscriminator // A unique, nonzero discriminator value
276 YourDiscriminator BFDDiscriminator // discriminator received from the remote system.
277 DesiredMinTxInterval BFDTimeInterval // Minimum interval, in microseconds, the local system would like to use when transmitting BFD Control packets
278 RequiredMinRxInterval BFDTimeInterval // Minimum interval, in microseconds, between received BFD Control packets that this system is capable of supporting
279 RequiredMinEchoRxInterval BFDTimeInterval // Minimum interval, in microseconds, between received BFD Echo packets that this system is capable of supporting
280 AuthHeader *BFDAuthHeader // Authentication data, variable length.
281}
282
283// Length returns the data length of a BFD Control message which
284// changes based on the presence and type of authentication
285// contained in the message
286func (d *BFD) Length() int {
287 if d.AuthPresent && (d.AuthHeader != nil) {
288 return bfdMinimumRecordSizeInBytes + d.AuthHeader.Length()
289 }
290
291 return bfdMinimumRecordSizeInBytes
292}
293
294// LayerType returns the layer type of the BFD object, which is LayerTypeBFD.
295func (d *BFD) LayerType() gopacket.LayerType {
296 return LayerTypeBFD
297}
298
299// decodeBFD analyses a byte slice and attempts to decode it as a BFD
300// control packet
301//
302// If it succeeds, it loads p with information about the packet and returns nil.
303// If it fails, it returns an error (non nil).
304//
305// This function is employed in layertypes.go to register the BFD layer.
306func decodeBFD(data []byte, p gopacket.PacketBuilder) error {
307
308 // Attempt to decode the byte slice.
309 d := &BFD{}
310 err := d.DecodeFromBytes(data, p)
311 if err != nil {
312 return err
313 }
314
315 // If the decoding worked, add the layer to the packet and set it
316 // as the application layer too, if there isn't already one.
317 p.AddLayer(d)
318 p.SetApplicationLayer(d)
319
320 return nil
321}
322
323// DecodeFromBytes analyses a byte slice and attempts to decode it as a BFD
324// control packet.
325//
326// Upon succeeds, it loads the BFD object with information about the packet
327// and returns nil.
328// Upon failure, it returns an error (non nil).
329func (d *BFD) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
330
331 // If the data block is too short to be a BFD record, then return an error.
332 if len(data) < bfdMinimumRecordSizeInBytes {
333 df.SetTruncated()
334 return errors.New("BFD packet too short")
335 }
336
337 pLen := uint8(data[3])
338 if len(data) != int(pLen) {
339 return errors.New("BFD packet length does not match")
340 }
341
342 // BFD type embeds type BaseLayer which contains two fields:
343 // Contents is supposed to contain the bytes of the data at this level.
344 // Payload is supposed to contain the payload of this level.
345 // Here we set the baselayer to be the bytes of the BFD record.
346 d.BaseLayer = BaseLayer{Contents: data[:len(data)]}
347
348 // Extract the fields from the block of bytes.
349 // To make sense of this, refer to the packet diagram
350 // above and the section on endian conventions.
351
352 // The first few fields are all packed into the first 32 bits. Unpack them.
353 d.Version = BFDVersion(((data[0] & 0xE0) >> 5))
354 d.Diagnostic = BFDDiagnostic(data[0] & 0x1F)
355 data = data[1:]
356
357 d.State = BFDState((data[0] & 0xC0) >> 6)
358 d.Poll = data[0]&0x20 != 0
359 d.Final = data[0]&0x10 != 0
360 d.ControlPlaneIndependent = data[0]&0x08 != 0
361 d.AuthPresent = data[0]&0x04 != 0
362 d.Demand = data[0]&0x02 != 0
363 d.Multipoint = data[0]&0x01 != 0
364 data = data[1:]
365
366 data, d.DetectMultiplier = data[1:], BFDDetectMultiplier(data[0])
367 data, _ = data[1:], uint8(data[0]) // Consume length
368
369 // The remaining fields can just be copied in big endian order.
370 data, d.MyDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4]))
371 data, d.YourDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4]))
372 data, d.DesiredMinTxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
373 data, d.RequiredMinRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
374 data, d.RequiredMinEchoRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
375
376 if d.AuthPresent && (len(data) > 2) {
377 d.AuthHeader = &BFDAuthHeader{}
378 data, d.AuthHeader.AuthType = data[1:], BFDAuthType(data[0])
379 data, _ = data[1:], uint8(data[0]) // Consume length
380 data, d.AuthHeader.KeyID = data[1:], BFDAuthKeyID(data[0])
381
382 switch d.AuthHeader.AuthType {
383 case BFDAuthTypePassword:
384 d.AuthHeader.Data = BFDAuthData(data)
385 case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
386 // Skipped reserved byte
387 data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5]))
388 d.AuthHeader.Data = BFDAuthData(data)
389 case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
390 // Skipped reserved byte
391 data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5]))
392 d.AuthHeader.Data = BFDAuthData(data)
393 }
394 }
395
396 return nil
397}
398
399// SerializeTo writes the serialized form of this layer into the
400// SerializationBuffer, implementing gopacket.SerializableLayer.
401// See the docs for gopacket.SerializableLayer for more info.
402func (d *BFD) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
403 data, err := b.PrependBytes(bfdMinimumRecordSizeInBytes)
404 if err != nil {
405 return err
406 }
407
408 // Pack the first few fields into the first 32 bits.
409 data[0] = byte(byte(d.Version<<5) | byte(d.Diagnostic))
410 h := uint8(0)
411 h |= (uint8(d.State) << 6)
412 h |= (uint8(bool2uint8(d.Poll)) << 5)
413 h |= (uint8(bool2uint8(d.Final)) << 4)
414 h |= (uint8(bool2uint8(d.ControlPlaneIndependent)) << 3)
415 h |= (uint8(bool2uint8(d.AuthPresent)) << 2)
416 h |= (uint8(bool2uint8(d.Demand)) << 1)
417 h |= uint8(bool2uint8(d.Multipoint))
418 data[1] = byte(h)
419 data[2] = byte(d.DetectMultiplier)
420 data[3] = byte(d.Length())
421
422 // The remaining fields can just be copied in big endian order.
423 binary.BigEndian.PutUint32(data[4:], uint32(d.MyDiscriminator))
424 binary.BigEndian.PutUint32(data[8:], uint32(d.YourDiscriminator))
425 binary.BigEndian.PutUint32(data[12:], uint32(d.DesiredMinTxInterval))
426 binary.BigEndian.PutUint32(data[16:], uint32(d.RequiredMinRxInterval))
427 binary.BigEndian.PutUint32(data[20:], uint32(d.RequiredMinEchoRxInterval))
428
429 if d.AuthPresent && (d.AuthHeader != nil) {
430 auth, err := b.AppendBytes(int(d.AuthHeader.Length()))
431 if err != nil {
432 return err
433 }
434
435 auth[0] = byte(d.AuthHeader.AuthType)
436 auth[1] = byte(d.AuthHeader.Length())
437 auth[2] = byte(d.AuthHeader.KeyID)
438
439 switch d.AuthHeader.AuthType {
440 case BFDAuthTypePassword:
441 copy(auth[3:], d.AuthHeader.Data)
442 case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
443 auth[3] = byte(0)
444 binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber))
445 copy(auth[8:], d.AuthHeader.Data)
446 case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
447 auth[3] = byte(0)
448 binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber))
449 copy(auth[8:], d.AuthHeader.Data)
450 }
451 }
452
453 return nil
454}
455
456// CanDecode returns a set of layers that BFD objects can decode.
457// As BFD objects can only decide the BFD layer, we can return just that layer.
458// Apparently a single layer type implements LayerClass.
459func (d *BFD) CanDecode() gopacket.LayerClass {
460 return LayerTypeBFD
461}
462
463// NextLayerType specifies the next layer that GoPacket should attempt to
464// analyse after this (BFD) layer. As BFD packets do not contain any payload
465// bytes, there are no further layers to analyse.
466func (d *BFD) NextLayerType() gopacket.LayerType {
467 return gopacket.LayerTypeZero
468}
469
470// Payload returns an empty byte slice as BFD packets do not carry a payload
471func (d *BFD) Payload() []byte {
472 return nil
473}
474
475// bool2uint8 converts a bool to uint8
476func bool2uint8(b bool) uint8 {
477 if b {
478 return 1
479 }
480 return 0
481}