Repository navigation
Expand file tree
/
Copy pathopen.go
More file actions
727 lines (614 loc) · 23.5 KB
/
Copy pathopen.go
File metadata and controls
727 lines (614 loc) · 23.5 KB
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
package bgp
import (
"encoding/binary"
"errors"
"fmt"
"math"
"time"
)
const (
// version is the BGP version implemented by this package, BGP-4.
version = 4
// asTrans is the reserved ASN AS_TRANS, sent in the fixed 2 byte ASN
// field of an OPEN message when a speaker's ASN cannot fit in 2 bytes,
// as described in RFC 6793.
asTrans = 23456
// paramCapabilities is the OPEN optional parameter type for capabilities,
// as described in RFC 5492.
paramCapabilities = 2
// minHoldTime is the smallest nonzero hold time RFC 4271, section 6.2
// permits a speaker to accept.
minHoldTime = 3 * time.Second
)
// An Open is a BGP OPEN message, the first message sent by each peer after a
// connection is established, as described in RFC 4271, section 4.2.
type Open struct {
// ASN is the speaker's autonomous system number. Four byte ASNs (RFC
// 6793) are handled natively: marshaling generates the Four-Octet AS
// Number capability and parsing consumes it, so it must not appear in
// Capabilities.
ASN uint32
// HoldTime proposes the session's hold time. It must be zero or at
// least 3 seconds, and is truncated to a whole number of seconds on
// the wire.
HoldTime time.Duration
// ID is the speaker's BGP identifier, unique within an autonomous
// system; see [Identifier].
ID Identifier
// Capabilities advertises the speaker's optional capabilities, as
// described in RFC 5492.
Capabilities []Capability
// FourOctetAS reports whether an OPEN carries the Four-Octet AS Number
// capability. ParseMessage sets it when it consumes the capability into
// ASN, and the OPEN this package builds for its own speaker carries it,
// since AppendBinary always advertises the capability. AppendBinary
// ignores the field itself, so a zero value marshals. The FSM rejects a
// peer without it, since ASN is otherwise ambiguous for four byte
// values.
FourOctetAS bool
}
func (*Open) messageType() MessageType { return MessageTypeOpen }
// AppendBinary implements encoding.BinaryAppender.
func (o *Open) AppendBinary(b []byte) ([]byte, error) {
if o.HoldTime != 0 && o.HoldTime < minHoldTime {
return nil, fmt.Errorf("bgp: OPEN hold time must be zero or at least %s: %s", minHoldTime, o.HoldTime)
}
holdTime := o.HoldTime / time.Second
if holdTime > math.MaxUint16 {
return nil, fmt.Errorf("bgp: OPEN hold time too large: %s", o.HoldTime)
}
// RFC 6286, section 2.1: an identifier must be nonzero.
if o.ID == 0 {
return nil, errors.New("bgp: OPEN BGP identifier must be nonzero")
}
// A 2 byte ASN is sent as-is. A larger ASN is replaced by the AS_TRANS
// placeholder, and conveyed in full by the Four-Octet AS Number
// capability, which is always advertised.
wireASN := o.ASN
if wireASN > math.MaxUint16 {
wireASN = asTrans
}
caps, err := appendCapability(nil, Capability{
Code: CapabilityFourOctetAS,
Data: binary.BigEndian.AppendUint32(nil, o.ASN),
})
if err != nil {
return nil, err
}
for _, c := range o.Capabilities {
if c.Code == CapabilityFourOctetAS {
return nil, errors.New("bgp: OPEN Four-Octet AS Number capability is generated automatically and must not be set")
}
caps, err = appendCapability(caps, c)
if err != nil {
return nil, err
}
}
// All capabilities reside in a single Capabilities optional parameter,
// and the one-byte total optional parameters length below also counts
// that parameter's own type and length bytes.
if len(caps) > math.MaxUint8-2 {
return nil, errors.New("bgp: OPEN capabilities too large")
}
b, off := appendHeader(b, MessageTypeOpen)
b = append(b, version)
b = binary.BigEndian.AppendUint16(b, uint16(wireASN))
b = binary.BigEndian.AppendUint16(b, uint16(holdTime))
b = binary.BigEndian.AppendUint32(b, uint32(o.ID))
b = append(b, byte(len(caps)+2), paramCapabilities, byte(len(caps)))
b = append(b, caps...)
return finishMessage(b, off)
}
// parseOpen parses the body of an OPEN message.
func parseOpen(b []byte) (*Open, error) {
if len(b) < 10 {
return nil, badLength(len(b), "OPEN message too short: %d byte body", len(b))
}
if v := b[0]; v != version {
// The diagnostic data is the largest version this package supports,
// per RFC 4271, section 6.2.
return nil, openError(SubcodeUnsupportedVersionNumber, []byte{0, version},
"unsupported BGP version %d", v)
}
o := &Open{
ASN: uint32(binary.BigEndian.Uint16(b[1:3])),
HoldTime: time.Duration(binary.BigEndian.Uint16(b[3:5])) * time.Second,
ID: Identifier(binary.BigEndian.Uint32(b[5:9])),
}
// RFC 4271, section 6.2: hold times of 1 or 2 seconds must be rejected.
if o.HoldTime != 0 && o.HoldTime < minHoldTime {
return nil, openError(SubcodeUnacceptableHoldTime, nil,
"OPEN hold time must be zero or at least %s: %s", minHoldTime, o.HoldTime)
}
// RFC 6286, section 2.1: an identifier must be nonzero.
if o.ID == 0 {
return nil, openError(SubcodeBadBGPIdentifier, nil,
"OPEN BGP identifier must be nonzero")
}
if int(b[9]) != len(b[10:]) {
return nil, openError(0, nil, "OPEN optional parameters length mismatch")
}
if err := o.parseParams(b[10:]); err != nil {
return nil, err
}
return o, nil
}
// parseParams parses an OPEN's optional parameters into o. Capabilities are
// the only supported parameter type.
func (o *Open) parseParams(params []byte) error {
for len(params) > 0 {
if len(params) < 2 {
return openError(0, nil, "OPEN optional parameter truncated")
}
typ, n := params[0], int(params[1])
if len(params[2:]) < n {
return openError(0, nil, "OPEN optional parameter truncated")
}
if typ != paramCapabilities {
return openError(SubcodeUnsupportedOptionalParameter, nil,
"unsupported OPEN optional parameter %d", typ)
}
if err := o.parseCapabilities(params[2 : 2+n]); err != nil {
return err
}
params = params[2+n:]
}
return nil
}
// parseCapabilities parses the contents of one Capabilities optional
// parameter into o.
func (o *Open) parseCapabilities(caps []byte) error {
for len(caps) > 0 {
if len(caps) < 2 {
return openError(0, nil, "OPEN capability truncated")
}
code, n := CapabilityCode(caps[0]), int(caps[1])
if len(caps[2:]) < n {
return openError(0, nil, "OPEN capability truncated")
}
c := Capability{
Code: code,
Data: caps[2 : 2+n : 2+n],
}
if code == CapabilityFourOctetAS {
// Consume the speaker's 4 byte ASN directly; see [Open.ASN].
if n != 4 {
return openError(0, nil, "invalid Four-Octet AS Number capability")
}
o.ASN = binary.BigEndian.Uint32(c.Data)
o.FourOctetAS = true
} else {
o.Capabilities = append(o.Capabilities, c)
}
caps = caps[2+n:]
}
return nil
}
// A CapabilityCode identifies the type of a Capability.
type CapabilityCode uint8
// CapabilityCode values, as assigned by IANA.
const (
CapabilityMultiprotocol CapabilityCode = 1
CapabilityRouteRefresh CapabilityCode = 2
CapabilityExtendedNextHop CapabilityCode = 5
CapabilityRole CapabilityCode = 9
CapabilityGracefulRestart CapabilityCode = 64
CapabilityFourOctetAS CapabilityCode = 65
CapabilityAddPath CapabilityCode = 69
CapabilityEnhancedRouteRefresh CapabilityCode = 70
CapabilityLongLivedGracefulRestart CapabilityCode = 71
CapabilityFQDN CapabilityCode = 73
)
// A Capability is a BGP capability in raw binary form, advertised in an Open
// message, as described in RFC 5492.
//
// When produced by ParseMessage, Data references the input buffer rather
// than copying it; see [ParseMessage].
type Capability struct {
Code CapabilityCode
Data []byte
}
// MultiprotocolCapability produces a Capability which advertises support for
// the address family f, as described in RFC 4760.
func MultiprotocolCapability(f Family) Capability {
data := binary.BigEndian.AppendUint16(make([]byte, 0, 4), uint16(f.AFI))
data = append(data, 0, byte(f.SAFI))
return Capability{
Code: CapabilityMultiprotocol,
Data: data,
}
}
// Multiprotocol parses the address family advertised by a
// CapabilityMultiprotocol Capability.
func (c Capability) Multiprotocol() (Family, error) {
if c.Code != CapabilityMultiprotocol {
return Family{}, fmt.Errorf("bgp: capability %d is not a multiprotocol capability", uint8(c.Code))
}
if len(c.Data) != 4 {
return Family{}, errors.New("bgp: invalid multiprotocol capability")
}
return Family{
AFI: AFI(binary.BigEndian.Uint16(c.Data[0:2])),
SAFI: SAFI(c.Data[3]),
}, nil
}
// ExtendedNextHopCapability produces a Capability which advertises the
// ability to receive routes for each address family in fs with an IPv6 next
// hop, as described in RFC 8950.
func ExtendedNextHopCapability(fs ...Family) Capability {
data := make([]byte, 0, 6*len(fs))
for _, f := range fs {
data = binary.BigEndian.AppendUint16(data, uint16(f.AFI))
data = binary.BigEndian.AppendUint16(data, uint16(f.SAFI))
data = binary.BigEndian.AppendUint16(data, uint16(AFIIPv6))
}
return Capability{
Code: CapabilityExtendedNextHop,
Data: data,
}
}
// ExtendedNextHop parses the address families for which a
// CapabilityExtendedNextHop Capability advertises IPv6 next hop support, as
// described in RFC 8950. The result is the families
// ExtendedNextHopCapability was given, after a wire round trip.
//
// A nil result is a well-formed capability advertising no families. Entries
// naming a next hop AFI other than IPv6 are skipped: RFC 8950 defines no
// such entries.
func (c Capability) ExtendedNextHop() ([]Family, error) {
if c.Code != CapabilityExtendedNextHop {
return nil, fmt.Errorf("bgp: capability %d is not an extended next hop capability", uint8(c.Code))
}
if len(c.Data)%6 != 0 {
return nil, errors.New("bgp: invalid extended next hop capability")
}
var fams []Family
for d := c.Data; len(d) > 0; d = d[6:] {
// Each entry is an AFI, SAFI, and next hop AFI triple. RFC 8950
// defines the capability for IPv6 next hops only, so an entry
// naming any other next hop AFI is undefined and is skipped
// rather than reported.
if AFI(binary.BigEndian.Uint16(d[4:6])) != AFIIPv6 {
continue
}
fams = append(fams, Family{
AFI: AFI(binary.BigEndian.Uint16(d[0:2])),
SAFI: SAFI(binary.BigEndian.Uint16(d[2:4])),
})
}
return fams, nil
}
// maxRestartTime is the largest restart time the graceful restart
// capability's 12 bit whole-seconds field can carry (RFC 4724, section 3).
const maxRestartTime = 4095 * time.Second
// A GracefulRestart is the decoded content of a graceful restart capability
// (RFC 4724, with the RFC 8538 N bit): a speaker's restart claims and the
// families whose forwarding state it preserves. This package carries the
// negotiation surface only. The behavior the capability negotiates, such as
// retaining a restarting peer's routes as stale, running the restart timer,
// and sweeping on End-of-RIB, belongs to the caller's RIB.
type GracefulRestart struct {
// Restarting is the Restart State (R) bit: the speaker has restarted,
// and this session is its first since.
Restarting bool
// NotificationSupport is the N bit (RFC 8538): the speaker supports
// graceful restart procedures across sessions ended by a NOTIFICATION,
// Hard Reset excepted.
NotificationSupport bool
// RestartTime is how long the peer should retain this speaker's routes
// while it is away: whole seconds, at most 4095, per the 12 bit wire
// field.
RestartTime time.Duration
// Families lists the families the speaker claims forwarding state for.
Families []GracefulRestartFamily
}
// A GracefulRestartFamily is one family entry of a graceful restart
// capability: an address family, and the speaker's claim that its forwarding
// state for the family survived the restart (the F bit).
type GracefulRestartFamily struct {
Family Family
ForwardingPreserved bool
}
// GracefulRestartCapability produces a Capability which advertises graceful
// restart. RestartTime must lie within [0, 4095s], the 12 bit whole-seconds
// wire field, and is truncated to whole seconds.
//
// A Peer or FSM advertises graceful restart through its configuration's
// GracefulRestart field, not by placing this Capability in Capabilities: the
// Restart State bit varies per session attempt, so the FSM owns the encoding.
func GracefulRestartCapability(gr GracefulRestart) (Capability, error) {
secs := gr.RestartTime / time.Second
if secs < 0 || secs > maxRestartTime/time.Second {
return Capability{}, fmt.Errorf("bgp: graceful restart time must be within [0, %s]: %s", maxRestartTime, gr.RestartTime)
}
b := binary.BigEndian.AppendUint16(make([]byte, 0, 2+4*len(gr.Families)), uint16(secs))
if gr.Restarting {
b[0] |= 0x80
}
if gr.NotificationSupport {
b[0] |= 0x40
}
for _, f := range gr.Families {
b = binary.BigEndian.AppendUint16(b, uint16(f.Family.AFI))
var flags byte
if f.ForwardingPreserved {
flags = 0x80
}
b = append(b, byte(f.Family.SAFI), flags)
}
return Capability{
Code: CapabilityGracefulRestart,
Data: b,
}, nil
}
// GracefulRestart parses the content of a CapabilityGracefulRestart
// Capability. The returned value never references Data, so it remains valid
// after the buffer Data references is reused.
func (c Capability) GracefulRestart() (GracefulRestart, error) {
if c.Code != CapabilityGracefulRestart {
return GracefulRestart{}, fmt.Errorf("bgp: capability %d is not a graceful restart capability", uint8(c.Code))
}
if len(c.Data) < 2 || (len(c.Data)-2)%4 != 0 {
return GracefulRestart{}, errors.New("bgp: invalid graceful restart capability")
}
gr := GracefulRestart{
Restarting: c.Data[0]&0x80 != 0,
NotificationSupport: c.Data[0]&0x40 != 0,
RestartTime: time.Duration(binary.BigEndian.Uint16(c.Data[0:2])&0x0fff) * time.Second,
}
for d := c.Data[2:]; len(d) > 0; d = d[4:] {
gr.Families = append(gr.Families, GracefulRestartFamily{
Family: Family{
AFI: AFI(binary.BigEndian.Uint16(d[0:2])),
SAFI: SAFI(d[2]),
},
ForwardingPreserved: d[3]&0x80 != 0,
})
}
return gr, nil
}
// maxLongLivedStaleTime is the largest stale time a long-lived graceful
// restart capability's 24 bit whole-seconds field can carry (RFC 9494,
// section 3).
const maxLongLivedStaleTime = 0xffffff * time.Second
// A LongLivedGracefulRestart is the decoded content of a long-lived graceful
// restart capability (RFC 9494): per family, a speaker's claim that its
// forwarding state survives a restart and how long its routes may be kept
// as stale after the graceful restart period ends. This package carries the
// negotiation surface only. The behavior the capability negotiates, such as
// retaining routes as long-lived stale, attaching LLGR_STALE and
// depreferencing, and running the long-lived stale timer, belongs to the
// caller's RIB.
//
// RFC 9494, section 4.1 requires the capability to be advertised alongside
// the graceful restart capability, and a speaker which advertises it alone
// is treated as not having advertised it. The core does not enforce what the
// caller advertises; the caller sets both.
type LongLivedGracefulRestart struct {
// Families lists the families the speaker claims long-lived forwarding
// state for.
Families []LongLivedGracefulRestartFamily
}
// A LongLivedGracefulRestartFamily is one family entry of a long-lived
// graceful restart capability: an address family, the speaker's claim that
// its forwarding state for the family survived the restart (the F bit), and
// the Long-lived Stale Time.
type LongLivedGracefulRestartFamily struct {
Family Family
ForwardingPreserved bool
// StaleTime is how long the peer may retain this speaker's routes for
// the family as long-lived stale, once the graceful restart period
// ends: whole seconds, at most 16777215, per the 24 bit wire field.
StaleTime time.Duration
}
// LongLivedGracefulRestartCapability produces a Capability which advertises
// long-lived graceful restart. Each StaleTime must lie within
// [0, 16777215s], the 24 bit whole-seconds wire field, and is truncated to
// whole seconds.
//
// A Peer or FSM advertises long-lived graceful restart through its
// configuration's LongLivedGracefulRestart field, not by placing this
// Capability in Capabilities.
func LongLivedGracefulRestartCapability(llgr LongLivedGracefulRestart) (Capability, error) {
b := make([]byte, 0, 7*len(llgr.Families))
for _, f := range llgr.Families {
secs := f.StaleTime / time.Second
if secs < 0 || secs > maxLongLivedStaleTime/time.Second {
return Capability{}, fmt.Errorf("bgp: long-lived graceful restart stale time must be within [0, %s]: %s", maxLongLivedStaleTime, f.StaleTime)
}
b = binary.BigEndian.AppendUint16(b, uint16(f.Family.AFI))
var flags byte
if f.ForwardingPreserved {
flags = 0x80
}
b = append(b, byte(f.Family.SAFI), flags)
b = append(b, byte(secs>>16), byte(secs>>8), byte(secs))
}
return Capability{
Code: CapabilityLongLivedGracefulRestart,
Data: b,
}, nil
}
// LongLivedGracefulRestart parses the content of a
// CapabilityLongLivedGracefulRestart Capability. The returned value never
// references Data, so it remains valid after the buffer Data references is
// reused.
func (c Capability) LongLivedGracefulRestart() (LongLivedGracefulRestart, error) {
if c.Code != CapabilityLongLivedGracefulRestart {
return LongLivedGracefulRestart{}, fmt.Errorf("bgp: capability %d is not a long-lived graceful restart capability", uint8(c.Code))
}
if len(c.Data)%7 != 0 {
return LongLivedGracefulRestart{}, errors.New("bgp: invalid long-lived graceful restart capability")
}
var llgr LongLivedGracefulRestart
for d := c.Data; len(d) > 0; d = d[7:] {
secs := uint32(d[4])<<16 | uint32(d[5])<<8 | uint32(d[6])
llgr.Families = append(llgr.Families, LongLivedGracefulRestartFamily{
Family: Family{
AFI: AFI(binary.BigEndian.Uint16(d[0:2])),
SAFI: SAFI(d[2]),
},
ForwardingPreserved: d[3]&0x80 != 0,
StaleTime: time.Duration(secs) * time.Second,
})
}
return llgr, nil
}
// An AddPathFamily is one family entry of an add-path capability (RFC
// 7911): an address family, and the directions in which the speaker is able
// to use multiple paths for it. In a Session's negotiated result the
// directions are what actually applies to this speaker; see
// [Session.AddPath].
type AddPathFamily struct {
// Family is the address family.
Family Family
// Send reports the ability to send multiple paths for the family:
// NLRI entries carrying path identifiers.
Send bool
// Receive reports the ability to receive multiple paths for the
// family.
Receive bool
}
// AddPathCapability produces a Capability which advertises the add-path
// extension (RFC 7911) for the given families. Every entry must name at
// least one direction: the wire encodes nothing else.
//
// An FSM or Peer advertises add-path through Identity.AddPath, not by
// placing this Capability in Capabilities: negotiation must know the
// configured directions to produce [Session.AddPath].
func AddPathCapability(fs ...AddPathFamily) (Capability, error) {
b := make([]byte, 0, 4*len(fs))
for _, f := range fs {
if !f.Send && !f.Receive {
return Capability{}, fmt.Errorf("bgp: add-path family %s must name at least one direction", f.Family)
}
b = binary.BigEndian.AppendUint16(b, uint16(f.Family.AFI))
b = append(b, byte(f.Family.SAFI))
var sr byte
if f.Receive {
sr |= 1
}
if f.Send {
sr |= 2
}
b = append(b, sr)
}
return Capability{
Code: CapabilityAddPath,
Data: b,
}, nil
}
// AddPath parses the families and directions a CapabilityAddPath Capability
// advertises, as described in RFC 7911, section 4. The result never
// references Data, so it remains valid after the buffer Data references is
// reused.
func (c Capability) AddPath() ([]AddPathFamily, error) {
if c.Code != CapabilityAddPath {
return nil, fmt.Errorf("bgp: capability %d is not an add-path capability", uint8(c.Code))
}
if len(c.Data)%4 != 0 {
return nil, errors.New("bgp: invalid add-path capability")
}
fs := make([]AddPathFamily, 0, len(c.Data)/4)
for d := c.Data; len(d) > 0; d = d[4:] {
sr := d[3]
if sr == 0 || sr > 3 {
return nil, fmt.Errorf("bgp: invalid add-path capability send/receive value %d", sr)
}
fs = append(fs, AddPathFamily{
Family: Family{
AFI: AFI(binary.BigEndian.Uint16(d[0:2])),
SAFI: SAFI(d[2]),
},
Send: sr&2 != 0,
Receive: sr&1 != 0,
})
}
return fs, nil
}
// FQDNCapability produces a Capability which advertises the speaker's
// hostname and domain name, as described in
// draft-walton-bgp-hostname-capability-02. The wire encoding is two
// length-prefixed UTF-8 strings, with lengths in bytes. Either string may
// be empty. FQDNCapability fails when the two strings together exceed 253
// bytes: with their length bytes, they must fit a capability's one-byte
// length.
//
// The capability is cosmetic: the draft says it SHOULD only be used to
// display a speaker's name when troubleshooting, and nothing in this
// package reads it beyond the codec.
func FQDNCapability(hostname, domain string) (Capability, error) {
if 2+len(hostname)+len(domain) > math.MaxUint8 {
return Capability{}, fmt.Errorf("bgp: FQDN capability hostname and domain name too large: %d bytes", len(hostname)+len(domain))
}
b := make([]byte, 0, 2+len(hostname)+len(domain))
b = append(b, byte(len(hostname)))
b = append(b, hostname...)
b = append(b, byte(len(domain)))
b = append(b, domain...)
return Capability{
Code: CapabilityFQDN,
Data: b,
}, nil
}
// FQDN parses the hostname and domain name a CapabilityFQDN Capability
// carries. The returned strings never reference Data, so they remain valid
// after the buffer Data references is reused.
func (c Capability) FQDN() (hostname, domain string, err error) {
if c.Code != CapabilityFQDN {
return "", "", fmt.Errorf("bgp: capability %d is not an FQDN capability", uint8(c.Code))
}
// Two length-prefixed strings, and nothing after them.
d := c.Data
if len(d) < 1 || len(d) < 1+int(d[0]) {
return "", "", errors.New("bgp: invalid FQDN capability")
}
hostname, d = string(d[1:1+int(d[0])]), d[1+int(d[0]):]
if len(d) < 1 || len(d) != 1+int(d[0]) {
return "", "", errors.New("bgp: invalid FQDN capability")
}
return hostname, string(d[1:]), nil
}
// RoleCapability produces a Capability which advertises the BGP Role r, as
// described in RFC 9234, section 4.1.
//
// A Peer or FSM advertises its role through Identity.Role, not by placing
// this Capability in Capabilities: negotiation must know the local role to
// check the peer's.
func RoleCapability(r Role) Capability {
return Capability{
Code: CapabilityRole,
Data: []byte{byte(r)},
}
}
// Role parses the BGP Role a CapabilityRole Capability advertises. An
// unassigned value is returned as is, not rejected: whether it is
// acceptable is negotiation's decision.
func (c Capability) Role() (Role, error) {
if c.Code != CapabilityRole {
return 0, fmt.Errorf("bgp: capability %d is not a BGP Role capability", uint8(c.Code))
}
if len(c.Data) != 1 {
return 0, errors.New("bgp: invalid BGP Role capability")
}
return Role(c.Data[0]), nil
}
// appendCapability appends the wire encoding of c to b.
func appendCapability(b []byte, c Capability) ([]byte, error) {
if len(c.Data) > math.MaxUint8 {
return nil, fmt.Errorf("bgp: capability %d data too large: %d bytes", uint8(c.Code), len(c.Data))
}
b = append(b, byte(c.Code), byte(len(c.Data)))
return append(b, c.Data...), nil
}
// mustAppendCapability appends the wire encoding of a locally generated,
// fixed-size capability, whose data always fits: an error is an invariant
// violation in this package, not an input. Caller-supplied capabilities go
// through appendCapability, whose error reaches the caller.
func mustAppendCapability(b []byte, c Capability) []byte {
b, err := appendCapability(b, c)
if err != nil {
panic(err)
}
return b
}