Repository navigation
Expand file tree
/
Copy pathattribute.go
More file actions
814 lines (691 loc) · 25.6 KB
/
Copy pathattribute.go
File metadata and controls
814 lines (691 loc) · 25.6 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
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
package bgp
import (
"encoding/binary"
"errors"
"fmt"
"math"
"net/netip"
"slices"
)
// AttrFlags describe a BGP path attribute, as described in RFC 4271,
// section 4.3.
type AttrFlags uint8
// AttrFlags values, as described in RFC 4271. The extended length flag is a
// wire encoding detail managed by this package: it is cleared when parsing
// and computed as needed when marshaling.
const (
AttrFlagOptional AttrFlags = 0x80
AttrFlagTransitive AttrFlags = 0x40
AttrFlagPartial AttrFlags = 0x20
attrFlagExtendedLength AttrFlags = 0x10
)
// An AttrType is the type of a BGP path attribute.
type AttrType uint8
// AttrType values, as assigned by IANA.
const (
AttrOrigin AttrType = 1
AttrASPath AttrType = 2
AttrNextHop AttrType = 3
AttrMED AttrType = 4
AttrLocalPref AttrType = 5
AttrAtomicAggregate AttrType = 6
AttrAggregator AttrType = 7
AttrCommunities AttrType = 8
AttrOriginatorID AttrType = 9
AttrClusterList AttrType = 10
AttrMPReachNLRI AttrType = 14
AttrMPUnreachNLRI AttrType = 15
AttrExtendedCommunities AttrType = 16
AttrLinkState AttrType = 29
AttrLargeCommunities AttrType = 32
AttrOTC AttrType = 35
)
// ErrUnknownAttribute is the error RawAttribute.Parse wraps for an attribute
// of a type this package does not interpret. It is a plain error, not a
// *MessageError: an unrecognized attribute is not a protocol error, and a
// caller tests for it with errors.Is to pass the attribute along in raw form
// rather than treat it as malformed.
var ErrUnknownAttribute = errors.New("bgp: cannot parse attribute of unknown type")
// ErrUnsupportedNextHop is the error RawAttribute.Parse wraps for an
// MP_REACH_NLRI of an unmodeled family whose next hop it cannot decode. It
// is not a *MessageError: the attribute is well formed, and a caller reads
// its next hop and NLRI raw.
var ErrUnsupportedNextHop = errors.New("bgp: unsupported next hop for an unmodeled address family")
// A RawAttribute is a BGP path attribute in raw binary form, as carried in an
// Update. Parse decodes a RawAttribute into one of this package's Attribute
// types, at the cost of additional allocations; callers which do not inspect
// attribute contents may skip parsing entirely.
//
// When produced by ParseMessage, Data references the input buffer rather
// than copying it; see [ParseMessage].
type RawAttribute struct {
Flags AttrFlags
Type AttrType
// addPath marks a multiprotocol attribute whose NLRI entries each
// carry a path identifier (RFC 7911): set by parseUpdate when the
// attribute's family is one the session negotiated add-path receive
// for, and consumed by Parse, which then produces PathPrefixes. Never
// set on an attribute a caller constructs: a sender chooses the
// encoding by the NLRI type it supplies. Declared before Data so it
// occupies alignment padding which exists regardless: a RIB retains
// these at full-table scale, and moving it after Data grows the
// struct by 8 bytes.
addPath bool
Data []byte
}
// Parse decodes a RawAttribute into a typed Attribute. Attributes of a type
// unknown to this package cannot be parsed, and remain available in raw form.
// Parsed Attributes never reference Data, and remain valid after the buffer
// Data references is reused.
//
// A malformed attribute of a known type produces a *MessageError which
// carries the erroneous attribute as its diagnostic data, per RFC 4271,
// section 6.3. An attribute of an unknown type produces an error wrapping
// [ErrUnknownAttribute], never a *MessageError: it is not a protocol error,
// and RFC 4271 requires unrecognized optional transitive attributes be passed
// along unmodified. A caller which must distinguish the two tests the error
// with errors.Is, and the RawAttribute itself remains usable in raw form.
// Likewise, an undecodable next hop of an unmodeled family produces
// [ErrUnsupportedNextHop]; a caller reads that attribute's next hop and
// NLRI raw from Data.
func (a RawAttribute) Parse() (Attribute, error) {
attr, err := a.parse()
if merr, ok := errors.AsType[*MessageError](err); ok {
return nil, a.echoData(merr)
}
return attr, err
}
// echoData attaches a to merr as the diagnostic data RFC 4271, section 6.3
// requires of an attribute error: flags, type, length, and value. An
// attribute framed in a real message always fits; the echo is omitted only
// when a caller constructed an attribute so large that the resulting
// NOTIFICATION could not be marshaled. A merr which already carries data
// keeps it.
func (a RawAttribute) echoData(merr *MessageError) *MessageError {
if merr == nil || merr.Data != nil {
return merr
}
data, err := appendRawAttribute(nil, a)
if err == nil && headerLen+2+len(data) <= MaxMessageSize {
merr.Data = data
}
return merr
}
// parse implements Parse, minus the diagnostic data handling. What is
// malformed is attrRule.validate's to say; what remains here is decoding.
func (a RawAttribute) parse() (Attribute, error) {
if merr := attrRules[a.Type].validate(&a); merr != nil {
return nil, merr
}
switch a.Type {
case AttrOrigin:
return Origin(a.Data[0]), nil
case AttrASPath:
return parseASPath(a.Data), nil
case AttrNextHop:
return NextHop(netip.AddrFrom4([4]byte(a.Data))), nil
case AttrMED:
return MED(binary.BigEndian.Uint32(a.Data)), nil
case AttrLocalPref:
return LocalPref(binary.BigEndian.Uint32(a.Data)), nil
case AttrAtomicAggregate:
return AtomicAggregate{}, nil
case AttrAggregator:
return Aggregator{
ASN: binary.BigEndian.Uint32(a.Data[0:4]),
ID: Identifier(binary.BigEndian.Uint32(a.Data[4:8])),
}, nil
case AttrCommunities:
cs := make(Communities, 0, len(a.Data)/4)
for i := 0; i < len(a.Data); i += 4 {
cs = append(cs, Community(binary.BigEndian.Uint32(a.Data[i:])))
}
return cs, nil
case AttrOriginatorID:
return OriginatorID(binary.BigEndian.Uint32(a.Data)), nil
case AttrClusterList:
cl := make(ClusterList, 0, len(a.Data)/4)
for i := 0; i < len(a.Data); i += 4 {
cl = append(cl, Identifier(binary.BigEndian.Uint32(a.Data[i:])))
}
return cl, nil
case AttrExtendedCommunities:
ecs := make(ExtendedCommunities, 0, len(a.Data)/8)
for i := 0; i < len(a.Data); i += 8 {
ecs = append(ecs, ExtendedCommunity([8]byte(a.Data[i:i+8])))
}
return ecs, nil
case AttrMPReachNLRI:
m, err := parseMPReachNLRI(a.Data, a.addPath)
if err != nil {
return nil, err
}
return m, nil
case AttrMPUnreachNLRI:
m, err := parseMPUnreachNLRI(a.Data, a.addPath)
if err != nil {
return nil, err
}
return m, nil
case AttrLargeCommunities:
ls := make(LargeCommunities, 0, len(a.Data)/12)
for i := 0; i < len(a.Data); i += 12 {
ls = append(ls, LargeCommunity{
Global: binary.BigEndian.Uint32(a.Data[i:]),
Local1: binary.BigEndian.Uint32(a.Data[i+4:]),
Local2: binary.BigEndian.Uint32(a.Data[i+8:]),
})
}
return ls, nil
case AttrOTC:
return OTC(binary.BigEndian.Uint32(a.Data)), nil
default:
return nil, fmt.Errorf("%w %d", ErrUnknownAttribute, uint8(a.Type))
}
}
// mpFamily returns the address family a multiprotocol attribute names in
// its first three data bytes. It returns false for an attribute of any
// other type, or one too short to name a family.
func (a RawAttribute) mpFamily() (Family, bool) {
if (a.Type != AttrMPReachNLRI && a.Type != AttrMPUnreachNLRI) || len(a.Data) < 3 {
return Family{}, false
}
return Family{
AFI: AFI(binary.BigEndian.Uint16(a.Data[0:2])),
SAFI: SAFI(a.Data[2]),
}, true
}
// RawAttribute implements Attribute so that an attribute this package does
// not interpret travels through MarshalAttributes unmodified, as RFC 4271,
// section 5 requires of unrecognized optional transitive attributes, and so
// that RawAttributes.Parse can return one in place of a typed value. Its
// flags are carried exactly as given.
func (a RawAttribute) attrType() AttrType { return a.Type }
func (a RawAttribute) attrFlags() AttrFlags { return a.Flags }
func (a RawAttribute) appendData(b []byte) ([]byte, error) {
return append(b, a.Data...), nil
}
// RawAttributes is the path attribute list of an Update, in raw binary form.
//
// Its Clone method is the retention primitive for callers which store
// attributes past the lifetime of the buffer they reference, most typically
// a RIB keeping a route's attributes, without cloning the whole Update.
type RawAttributes []RawAttribute
// Clone returns a deep copy of the attribute list which shares no memory
// with the original: each attribute's Data survives reuse of the buffer a
// parsed message references. A nil list clones to nil.
func (as RawAttributes) Clone() RawAttributes {
if as == nil {
return nil
}
cs := make(RawAttributes, len(as))
for i := range as {
cs[i] = *as[i].Clone()
}
return cs
}
// Find returns the first attribute of type t in as, in raw form: the lookup
// for a caller which only needs to test presence, or which needs an
// attribute this package does not interpret. Lookup is the typed form.
func (as RawAttributes) Find(t AttrType) (RawAttribute, bool) {
for _, a := range as {
if a.Type == t {
return a, true
}
}
return RawAttribute{}, false
}
// Parse decodes every attribute in as into typed form, in list order. An
// attribute of a type this package does not interpret is returned as the
// RawAttribute itself, which implements Attribute: nothing is dropped, and
// MarshalAttributes passes such an attribute along unmodified, as RFC 4271,
// section 5 requires of unrecognized optional transitive attributes.
//
// A malformed attribute of a known type fails the whole parse with its
// *MessageError; see [RawAttribute.Parse]. The [ErrUnknownAttribute] and
// [ErrUnsupportedNextHop] errors the single-attribute Parse returns are
// absorbed here: each is the signal to keep the attribute raw, not a failure.
// Typed values never reference the raw list's Data, but a RawAttribute
// returned as-is does, so a caller retaining the result past the lifetime of
// the buffer the list references clones the list first.
func (as RawAttributes) Parse() ([]Attribute, error) {
if as == nil {
return nil, nil
}
attrs := make([]Attribute, 0, len(as))
for _, a := range as {
attr, err := a.Parse()
switch {
case errors.Is(err, ErrUnknownAttribute), errors.Is(err, ErrUnsupportedNextHop):
attr = a
case err != nil:
return nil, err
}
attrs = append(attrs, attr)
}
return attrs, nil
}
// markAddPath marks the multiprotocol attributes of the add-path receive
// families fs, so a later typed parse decodes their path identifiers
// without the caller re-supplying the negotiation; see RawAttribute.addPath.
// A short attribute is left unmarked for its parse to reject.
func (as RawAttributes) markAddPath(fs []Family) {
if len(fs) == 0 {
return
}
for i, a := range as {
if f, ok := a.mpFamily(); ok && slices.Contains(fs, f) {
as[i].addPath = true
}
}
}
// Lookup finds the first attribute of as whose type is T's and parses it,
// reporting false when as carries none: the typed accessor for one attribute
// of an Update, as in Lookup[ASPath](u.Attributes). A malformed attribute
// produces the *MessageError of RawAttribute.Parse. On
// [ErrUnsupportedNextHop], read the attribute raw with RawAttributes.Find.
//
// T must be one of the typed Attribute implementations, which each know
// their own wire type. The Attribute interface itself is rejected with an
// error, and a RawAttribute has no type of its own to look up, so
// Lookup[RawAttribute] finds nothing; RawAttributes.Find serves both.
func Lookup[T Attribute](as RawAttributes) (T, bool, error) {
var zero T
if any(zero) == nil {
return zero, false, errors.New("bgp: Lookup requires a concrete attribute type, not the Attribute interface")
}
raw, ok := as.Find(zero.attrType())
if !ok {
return zero, false, nil
}
attr, err := raw.Parse()
if err != nil {
return zero, false, err
}
return attr.(T), true, nil
}
// appendRawAttribute appends the wire encoding of a to b: flags, type,
// length, and data.
func appendRawAttribute(b []byte, a RawAttribute) ([]byte, error) {
if len(a.Data) > math.MaxUint16 {
return nil, fmt.Errorf("bgp: attribute %d data too large: %d bytes", uint8(a.Type), len(a.Data))
}
flags := a.Flags &^ attrFlagExtendedLength
if len(a.Data) > math.MaxUint8 {
flags |= attrFlagExtendedLength
}
b = append(b, byte(flags), byte(a.Type))
if flags&attrFlagExtendedLength != 0 {
b = binary.BigEndian.AppendUint16(b, uint16(len(a.Data)))
} else {
b = append(b, byte(len(a.Data)))
}
return append(b, a.Data...), nil
}
// parseRawAttributes parses raw path attributes from b, until b is
// exhausted. The parsed attributes reference b rather than copying it; see
// ParseMessage.
//
// A truncated list returns the attributes read before the truncation
// alongside the error, so that RFC 7606, section 4 can classify the UPDATE
// with them: the Total Attribute Length still locates the NLRI. A caller
// which does not classify treats the error as fatal.
func parseRawAttributes(b []byte) (RawAttributes, *MessageError) {
// One counting pass sizes the slice exactly, so an attribute list
// costs a single allocation instead of append's doubling. Truncation
// is left to the parse loop below, which reports it precisely.
var count int
for r := b; len(r) >= 3; count++ {
var n int
if AttrFlags(r[0])&attrFlagExtendedLength != 0 {
if len(r) < 4 {
break
}
n = 4 + int(binary.BigEndian.Uint16(r[2:4]))
} else {
n = 3 + int(r[2])
}
if n > len(r) {
break
}
r = r[n:]
}
var attrs RawAttributes
if count > 0 {
attrs = make(RawAttributes, 0, count)
}
for len(b) > 0 {
if len(b) < 3 {
return attrs, truncatedAttribute()
}
flags, typ := AttrFlags(b[0]), AttrType(b[1])
var n int
if flags&attrFlagExtendedLength != 0 {
if len(b) < 4 {
return attrs, truncatedAttribute()
}
n = int(binary.BigEndian.Uint16(b[2:4]))
b = b[4:]
} else {
n = int(b[2])
b = b[3:]
}
if len(b) < n {
return attrs, truncatedAttribute()
}
attrs = append(attrs, RawAttribute{
Flags: flags &^ attrFlagExtendedLength,
Type: typ,
Data: b[:n:n],
})
b = b[n:]
}
return attrs, nil
}
// truncatedAttribute is the error for an attribute list which does not
// frame, either condition of RFC 7606, section 4.
func truncatedAttribute() *MessageError {
return updateError(SubcodeMalformedAttributeList, nil, "path attribute truncated")
}
// An Attribute is a BGP path attribute in parsed form. Attribute is
// implemented by Origin, ASPath, NextHop, MED, LocalPref,
// AtomicAggregate, Aggregator, Communities, OriginatorID, ClusterList,
// ExtendedCommunities, MPReachNLRI, MPUnreachNLRI, LargeCommunities, and
// OTC, and by RawAttribute itself, so that an attribute this
// package does not interpret can ride alongside typed ones.
type Attribute interface {
// attrType and attrFlags are the wire type and canonical flags of the
// Attribute. appendData appends the Attribute's wire encoded data.
// These also constrain the set of types which may implement Attribute.
attrType() AttrType
attrFlags() AttrFlags
appendData(b []byte) ([]byte, error)
}
var (
_ Attribute = Origin(0)
_ Attribute = ASPath(nil)
_ Attribute = NextHop{}
_ Attribute = MED(0)
_ Attribute = LocalPref(0)
_ Attribute = AtomicAggregate{}
_ Attribute = Aggregator{}
_ Attribute = Communities(nil)
_ Attribute = OriginatorID(0)
_ Attribute = ClusterList(nil)
_ Attribute = ExtendedCommunities(nil)
_ Attribute = MPReachNLRI{}
_ Attribute = MPUnreachNLRI{}
_ Attribute = LargeCommunities(nil)
_ Attribute = OTC(0)
_ Attribute = RawAttribute{}
)
// MarshalAttributes converts typed Attributes into raw form, for use in an
// Update message.
func MarshalAttributes(attrs ...Attribute) (RawAttributes, error) {
ras := make(RawAttributes, 0, len(attrs))
for _, a := range attrs {
data, err := a.appendData(nil)
if err != nil {
return nil, err
}
ras = append(ras, RawAttribute{
Flags: a.attrFlags(),
Type: a.attrType(),
Data: data,
})
}
return ras, nil
}
// An Origin is the ORIGIN attribute, describing how the routes in an Update
// were originally learned.
type Origin uint8
// Origin values, as described in RFC 4271.
const (
OriginIGP Origin = 0
OriginEGP Origin = 1
OriginIncomplete Origin = 2
)
// String returns the name of an Origin.
func (o Origin) String() string {
switch o {
case OriginIGP:
return "IGP"
case OriginEGP:
return "EGP"
case OriginIncomplete:
return "incomplete"
default:
return fmt.Sprintf("unknown(%d)", uint8(o))
}
}
func (Origin) attrType() AttrType { return AttrOrigin }
func (Origin) attrFlags() AttrFlags { return AttrFlagTransitive }
func (o Origin) appendData(b []byte) ([]byte, error) {
if o > OriginIncomplete {
return nil, fmt.Errorf("bgp: invalid origin %d", uint8(o))
}
return append(b, byte(o)), nil
}
// Wire values for AS path segment types. The confederation types are RFC
// 5065, section 3.
const (
asSet = 1
asSequence = 2
asConfedSequence = 3
asConfedSet = 4
)
// An ASSegment is a single segment of an ASPath. Set and Confed select
// among the four wire segment types: the zero value is an ordered
// AS_SEQUENCE.
type ASSegment struct {
// Set indicates that the segment is unordered: an AS_SET, or an
// AS_CONFED_SET when Confed is also true.
Set bool
// Confed indicates that the segment is a confederation segment per RFC
// 5065: an AS_CONFED_SEQUENCE, or an AS_CONFED_SET when Set is also
// true. Confederation segments are exchanged only within a
// confederation, and this package only carries them. Their section 5.3
// semantics are the caller's RIB's: a best-path AS_PATH length excludes
// confederation segments, the MED neighbor AS skips them, and
// confederation loop detection examines them.
Confed bool
// ASNs lists the autonomous systems within the segment.
ASNs []uint32
}
// An OriginAS is the origin of a route as its AS_PATH names it, derived
// per RFC 6811, section 2, for route origin validation. Exactly one of the
// three readings applies: ASN names the origin; Set reports that the path
// ends in an AS_SET, whose origin is RFC 6811's "NONE" and matches no
// authorization; Empty reports a path which names no origin at all, one
// with no autonomous system outside confederation segments, whose origin
// is the receiving speaker's own AS or confederation, which the path
// cannot know.
type OriginAS struct {
// ASN is the rightmost autonomous system of the path's final
// AS_SEQUENCE, or zero when Set or Empty.
ASN uint32
// Set reports that the path's final non-confederation segment is an
// AS_SET.
Set bool
// Empty reports a path with no autonomous system outside
// confederation segments.
Empty bool
}
// An ASPath is the AS_PATH attribute: the autonomous systems through which
// routing information in an Update has passed.
//
// ASNs are always encoded in four-octet form, per RFC 6793. Sessions with
// speakers which do not support the Four-Octet AS Number capability are not
// supported by this package.
type ASPath []ASSegment
// Origin returns the origin autonomous system the path names, per RFC 6811,
// section 2. Confederation segments never name an origin: they are the
// path's intra-confederation record, so they are skipped, and a path of
// only confederation segments reads Empty, since it originated within the
// local confederation. Validation of the origin against RPKI data is the
// caller's; see [ValidationState] for carrying its result.
func (p ASPath) Origin() OriginAS {
for _, last := range slices.Backward(p) {
switch {
case last.Confed:
continue
case last.Set:
return OriginAS{Set: true}
case len(last.ASNs) == 0:
// Not encodable, but representable: nothing names an origin.
return OriginAS{Empty: true}
default:
return OriginAS{ASN: last.ASNs[len(last.ASNs)-1]}
}
}
return OriginAS{Empty: true}
}
func (ASPath) attrType() AttrType { return AttrASPath }
func (ASPath) attrFlags() AttrFlags { return AttrFlagTransitive }
func (p ASPath) appendData(b []byte) ([]byte, error) {
for _, s := range p {
if len(s.ASNs) == 0 {
return nil, errors.New("bgp: AS path segment must contain at least 1 ASN")
}
// An AS_SET or AS_CONFED_SET is unordered and cannot be split
// without changing its meaning.
if s.Set && len(s.ASNs) > math.MaxUint8 {
return nil, fmt.Errorf("bgp: AS set segment must contain between 1 and 255 ASNs: %d", len(s.ASNs))
}
typ := byte(asSequence)
switch {
case s.Set && s.Confed:
typ = asConfedSet
case s.Set:
typ = asSet
case s.Confed:
typ = asConfedSequence
}
// A wire segment holds at most 255 ASNs; a longer sequence is
// encoded as multiple segments, per RFC 4271, section 4.3.
for asns := s.ASNs; len(asns) > 0; {
n := min(len(asns), math.MaxUint8)
b = append(b, typ, byte(n))
for _, asn := range asns[:n] {
b = binary.BigEndian.AppendUint32(b, asn)
}
asns = asns[n:]
}
}
return b, nil
}
// parseASPath parses AS_PATH data which validateASPath has framed.
func parseASPath(b []byte) ASPath {
var p ASPath
for len(b) > 0 {
typ, n := b[0], int(b[1])
var set, confed bool
switch typ {
case asSet:
set = true
case asConfedSequence:
confed = true
case asConfedSet:
set, confed = true, true
}
b = b[2:]
asns := make([]uint32, 0, n)
for i := range n {
asns = append(asns, binary.BigEndian.Uint32(b[4*i:]))
}
p = append(p, ASSegment{
Set: set,
Confed: confed,
ASNs: asns,
})
b = b[4*n:]
}
return p
}
// A NextHop is the NEXT_HOP attribute: the IPv4 address of the router to be
// used as the next hop to the routes advertised in an Update. Next hops for
// other address families are carried by MPReachNLRI.
type NextHop netip.Addr
// String returns the string form of a NextHop's address.
func (n NextHop) String() string { return netip.Addr(n).String() }
func (NextHop) attrType() AttrType { return AttrNextHop }
func (NextHop) attrFlags() AttrFlags { return AttrFlagTransitive }
func (n NextHop) appendData(b []byte) ([]byte, error) {
addr := netip.Addr(n).Unmap()
if !addr.Is4() {
return nil, fmt.Errorf("bgp: next hop must be an IPv4 address: %s", addr)
}
a := addr.As4()
return append(b, a[:]...), nil
}
// A MED is the MULTI_EXIT_DISC (Multi-Exit Discriminator) attribute, used to
// discriminate among multiple entry points to a neighboring autonomous
// system.
type MED uint32
func (MED) attrType() AttrType { return AttrMED }
func (MED) attrFlags() AttrFlags { return AttrFlagOptional }
func (m MED) appendData(b []byte) ([]byte, error) {
return binary.BigEndian.AppendUint32(b, uint32(m)), nil
}
// A LocalPref is the LOCAL_PREF attribute: a speaker's degree of preference
// for a route, exchanged between peers within an autonomous system.
type LocalPref uint32
func (LocalPref) attrType() AttrType { return AttrLocalPref }
func (LocalPref) attrFlags() AttrFlags { return AttrFlagTransitive }
func (l LocalPref) appendData(b []byte) ([]byte, error) {
return binary.BigEndian.AppendUint32(b, uint32(l)), nil
}
// An AtomicAggregate is the ATOMIC_AGGREGATE attribute, indicating that a
// route was selected over a more specific route which it covers.
type AtomicAggregate struct{}
func (AtomicAggregate) attrType() AttrType { return AttrAtomicAggregate }
func (AtomicAggregate) attrFlags() AttrFlags { return AttrFlagTransitive }
func (AtomicAggregate) appendData(b []byte) ([]byte, error) { return b, nil }
// An Aggregator is the AGGREGATOR attribute, identifying the autonomous
// system and router which aggregated a route. The ASN is always encoded in
// four-octet form, per RFC 6793.
type Aggregator struct {
// ASN is the autonomous system number of the aggregating speaker.
ASN uint32
// ID is the BGP identifier of the aggregating speaker.
ID Identifier
}
func (Aggregator) attrType() AttrType { return AttrAggregator }
func (Aggregator) attrFlags() AttrFlags { return AttrFlagOptional | AttrFlagTransitive }
func (a Aggregator) appendData(b []byte) ([]byte, error) {
b = binary.BigEndian.AppendUint32(b, a.ASN)
return binary.BigEndian.AppendUint32(b, uint32(a.ID)), nil
}
// An OriginatorID is the ORIGINATOR_ID attribute: the BGP identifier of the
// route's originator in the local autonomous system, added by a route
// reflector, as described in RFC 4456.
type OriginatorID Identifier
// String returns the conventional dotted quad form of an OriginatorID.
func (o OriginatorID) String() string { return Identifier(o).String() }
func (OriginatorID) attrType() AttrType { return AttrOriginatorID }
func (OriginatorID) attrFlags() AttrFlags { return AttrFlagOptional }
func (o OriginatorID) appendData(b []byte) ([]byte, error) {
return binary.BigEndian.AppendUint32(b, uint32(o)), nil
}
// A ClusterList is the CLUSTER_LIST attribute: the sequence of route
// reflection clusters a route has passed through, as described in RFC 4456.
// A cluster's identifier is by default its reflector's BGP identifier.
type ClusterList []Identifier
func (ClusterList) attrType() AttrType { return AttrClusterList }
func (ClusterList) attrFlags() AttrFlags { return AttrFlagOptional }
func (cl ClusterList) appendData(b []byte) ([]byte, error) {
for _, id := range cl {
b = binary.BigEndian.AppendUint32(b, uint32(id))
}
return b, nil
}
// An OTC is the OTC (Only to Customer) attribute: the autonomous system
// beyond which a route must only propagate toward customers, used to detect
// and prevent route leaks, as described in RFC 9234. [Role.Ingress] and
// [Role.Egress] decide when to add one and when its presence marks a leak.
type OTC uint32
func (OTC) attrType() AttrType { return AttrOTC }
func (OTC) attrFlags() AttrFlags { return AttrFlagOptional | AttrFlagTransitive }
func (o OTC) appendData(b []byte) ([]byte, error) {
return binary.BigEndian.AppendUint32(b, uint32(o)), nil
}