Newer
Older
func decodePathAttrs(buf *bytes.Buffer, tpal uint16, opt *Options) (*PathAttribute, error) {
var ret *PathAttribute
var eol *PathAttribute
var pa *PathAttribute
var err error
var consumed uint16
p := uint16(0)
for p < tpal {
pa, consumed, err = decodePathAttr(buf, opt)
if err != nil {
return nil, fmt.Errorf("Unable to decode path attr: %v", err)
}
p += consumed
if ret == nil {
ret = pa
eol = pa
} else {
eol.Next = pa
eol = pa
}
}
return ret, nil
}
func decodePathAttr(buf *bytes.Buffer, opt *Options) (pa *PathAttribute, consumed uint16, err error) {
pa = &PathAttribute{}
err = decodePathAttrFlags(buf, pa)
if err != nil {
return nil, consumed, fmt.Errorf("Unable to get path attribute flags: %v", err)
}
consumed++
err = decode(buf, []interface{}{&pa.TypeCode})
if err != nil {
return nil, consumed, err
}
consumed++
n, err := pa.setLength(buf)
if err != nil {
return nil, consumed, err
}
consumed += uint16(n)
switch pa.TypeCode {
case OriginAttr:
if err := pa.decodeOrigin(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode Origin: %v", err)
}
case ASPathAttr:
asnLength := uint8(2)
if opt.Supports4OctetASN {
asnLength = 4
}
if err := pa.decodeASPath(buf, asnLength); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode AS Path: %v", err)
}
case AS4PathAttr:
if err := pa.decodeASPath(buf, 4); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode AS4 Path: %v", err)
}
case NextHopAttr:
if err := pa.decodeNextHop(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode Next-Hop: %v", err)
}
case MEDAttr:
if err := pa.decodeMED(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode MED: %v", err)
}
case LocalPrefAttr:
if err := pa.decodeLocalPref(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode local pref: %v", err)
}
case AggregatorAttr:
if err := pa.decodeAggregator(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode Aggregator: %v", err)
}
case AtomicAggrAttr:
// Nothing to do for 0 octet long attribute
case CommunitiesAttr:
if err := pa.decodeCommunities(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode Community: %v", err)
}
case AS4AggregatorAttr:
if err := pa.decodeAS4Aggregator(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to skip not supported AS4Aggregator: %v", err)
}
if err := pa.decodeLargeCommunities(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode large communities: %v", err)
}
if err := pa.decodeUnknown(buf); err != nil {
return nil, consumed, fmt.Errorf("Failed to decode unknown attribute: %v", err)
}
func (pa *PathAttribute) decodeUnknown(buf *bytes.Buffer) error {
u := make([]byte, pa.Length)
p := uint16(0)
err := decode(buf, []interface{}{&u})
if err != nil {
return fmt.Errorf("Unable to decode: %v", err)
}
pa.Value = u
p += pa.Length
return nil
}
func (pa *PathAttribute) decodeOrigin(buf *bytes.Buffer) error {
origin := uint8(0)
p := uint16(0)
err := decode(buf, []interface{}{&origin})
if err != nil {
return fmt.Errorf("Unable to decode: %v", err)
}
pa.Value = origin
p++
return dumpNBytes(buf, pa.Length-p)
}
func (pa *PathAttribute) decodeASPath(buf *bytes.Buffer, asnLength uint8) error {
pa.Value = make(ASPath, 0)
p := uint16(0)
for p < pa.Length {
err := decode(buf, []interface{}{&segment.Type, &segment.Count})
if err != nil {
return err
}
p += 2
if segment.Type != ASSet && segment.Type != ASSequence {
return fmt.Errorf("Invalid AS Path segment type: %d", segment.Type)
}
if segment.Count == 0 {
return fmt.Errorf("Invalid AS Path segment length: %d", segment.Count)
}
segment.ASNs = make([]uint32, segment.Count)
asn, err := pa.decodeASN(buf, asnLength)
pa.Value = append(pa.Value.(ASPath), segment)
}
return nil
}
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
func (pa *PathAttribute) decodeASN(buf *bytes.Buffer, asnSize uint8) (asn uint32, err error) {
if asnSize == 4 {
return pa.decode4ByteASN(buf)
}
return pa.decode2ByteASN(buf)
}
func (pa *PathAttribute) decode4ByteASN(buf *bytes.Buffer) (asn uint32, err error) {
asn4 := uint32(0)
err = decode(buf, []interface{}{&asn4})
if err != nil {
return 0, err
}
return uint32(asn4), nil
}
func (pa *PathAttribute) decode2ByteASN(buf *bytes.Buffer) (asn uint32, err error) {
asn4 := uint16(0)
err = decode(buf, []interface{}{&asn4})
if err != nil {
return 0, err
}
return uint32(asn4), nil
}
return pa.decodeUint32(buf, "next hop")
}
func (pa *PathAttribute) decodeMED(buf *bytes.Buffer) error {
return pa.decodeUint32(buf, "MED")
}
func (pa *PathAttribute) decodeLocalPref(buf *bytes.Buffer) error {
return pa.decodeUint32(buf, "local pref")
}
func (pa *PathAttribute) decodeAggregator(buf *bytes.Buffer) error {
aggr := Aggretator{}
p := uint16(0)
err := decode(buf, []interface{}{&aggr.ASN})
if err != nil {
return err
}
p += 2
addr := [4]byte{}
n, err := buf.Read(addr[:])
return fmt.Errorf("Unable to read next hop: buf.Read read %d bytes", n)
aggr.Addr = fourBytesToUint32(addr)
p += 4
func (pa *PathAttribute) decodeCommunities(buf *bytes.Buffer) error {
return fmt.Errorf("Unable to read community path attribute. Length %d is not divisible by 4", pa.Length)
count := pa.Length / CommunityLen
coms := make([]uint32, count)
for i := uint16(0); i < count; i++ {
v, err := read4BytesAsUint32(buf)
func (pa *PathAttribute) decodeLargeCommunities(buf *bytes.Buffer) error {
if pa.Length%LargeCommunityLen != 0 {
return fmt.Errorf("Unable to read large community path attribute. Length %d is not divisible by 12", pa.Length)
}
count := pa.Length / LargeCommunityLen
coms := make([]LargeCommunity, count)
for i := uint16(0); i < count; i++ {
com := LargeCommunity{}
v, err := read4BytesAsUint32(buf)
if err != nil {
return err
}
com.GlobalAdministrator = v
if err != nil {
return err
}
com.DataPart1 = v
if err != nil {
return err
}
com.DataPart2 = v
coms[i] = com
}
pa.Value = coms
func (pa *PathAttribute) decodeAS4Aggregator(buf *bytes.Buffer) error {
return pa.decodeUint32(buf, "AS4Aggregator")
}
func (pa *PathAttribute) decodeUint32(buf *bytes.Buffer, attrName string) error {
v, err := read4BytesAsUint32(buf)
if err != nil {
return fmt.Errorf("Unable to decode %s: %v", attrName, err)
p := uint16(4)
err = dumpNBytes(buf, pa.Length-p)
if err != nil {
return fmt.Errorf("dumpNBytes failed: %v", err)
}
return nil
}
func (pa *PathAttribute) setLength(buf *bytes.Buffer) (int, error) {
bytesRead := 0
if pa.ExtendedLength {
err := decode(buf, []interface{}{&pa.Length})
if err != nil {
return 0, err
}
bytesRead = 2
} else {
x := uint8(0)
err := decode(buf, []interface{}{&x})
if err != nil {
return 0, err
}
pa.Length = uint16(x)
bytesRead = 1
}
return bytesRead, nil
}
func (a *PathAttribute) CommunityString() string {
s := ""
for _, com := range a.Value.([]uint32) {
s += CommunityStringForUint32(com) + " "
}
return strings.TrimRight(s, " ")
}
func (a *PathAttribute) LargeCommunityString() string {
s := ""
for _, com := range a.Value.([]LargeCommunity) {
s += com.String() + " "
}
return strings.TrimRight(s, " ")
}
// dumpNBytes is used to dump n bytes of buf. This is useful in case an path attributes
// length doesn't match a fixed length's attributes length (e.g. ORIGIN is always an octet)
func dumpNBytes(buf *bytes.Buffer, n uint16) error {
if n <= 0 {
return nil
}
dump := make([]byte, n)
err := decode(buf, []interface{}{&dump})
if err != nil {
return err
}
return nil
}
func (pa *PathAttribute) serialize(buf *bytes.Buffer, opt *Options) uint8 {
pathAttrLen := uint8(0)
switch pa.TypeCode {
case OriginAttr:
pathAttrLen = pa.serializeOrigin(buf)
case ASPathAttr:
case NextHopAttr:
pathAttrLen = pa.serializeNextHop(buf)
case MEDAttr:
pathAttrLen = pa.serializeMED(buf)
case LocalPrefAttr:
pathAttrLen = pa.serializeLocalpref(buf)
case AtomicAggrAttr:
pathAttrLen = pa.serializeAtomicAggregate(buf)
case AggregatorAttr:
pathAttrLen = pa.serializeAggregator(buf)
case CommunitiesAttr:
pathAttrLen = pa.serializeCommunities(buf)
pathAttrLen = pa.serializeLargeCommunities(buf)
}
return pathAttrLen
}
func (pa *PathAttribute) serializeOrigin(buf *bytes.Buffer) uint8 {
attrFlags := uint8(0)
attrFlags = setTransitive(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(OriginAttr)
length := uint8(1)
buf.WriteByte(length)
buf.WriteByte(pa.Value.(uint8))
return 4
}
func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer, opt *Options) uint8 {
attrFlags := uint8(0)
attrFlags = setTransitive(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(ASPathAttr)
asnLength := uint8(2)
if opt.Supports4OctetASN {
asnLength = 4
}
length := uint8(0)
segmentsBuf := bytes.NewBuffer(nil)
for _, segment := range pa.Value.(ASPath) {
segmentsBuf.WriteByte(segment.Type)
segmentsBuf.WriteByte(uint8(len(segment.ASNs)))
if asnLength == 2 {
segmentsBuf.Write(convert.Uint16Byte(uint16(asn)))
} else {
segmentsBuf.Write(convert.Uint32Byte(asn))
}
fmt.Println(segment.ASNs)
length += 2 + uint8(len(segment.ASNs))*asnLength
buf.WriteByte(length)
buf.Write(segmentsBuf.Bytes())
return length + 2
}
func (pa *PathAttribute) serializeNextHop(buf *bytes.Buffer) uint8 {
attrFlags := uint8(0)
attrFlags = setTransitive(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(NextHopAttr)
length := uint8(4)
buf.WriteByte(length)
addr := pa.Value.(uint32)
buf.Write(convert.Uint32Byte(addr))
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
return 7
}
func (pa *PathAttribute) serializeMED(buf *bytes.Buffer) uint8 {
attrFlags := uint8(0)
attrFlags = setOptional(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(MEDAttr)
length := uint8(4)
buf.WriteByte(length)
buf.Write(convert.Uint32Byte(pa.Value.(uint32)))
return 7
}
func (pa *PathAttribute) serializeLocalpref(buf *bytes.Buffer) uint8 {
attrFlags := uint8(0)
attrFlags = setTransitive(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(LocalPrefAttr)
length := uint8(4)
buf.WriteByte(length)
buf.Write(convert.Uint32Byte(pa.Value.(uint32)))
return 7
}
func (pa *PathAttribute) serializeAtomicAggregate(buf *bytes.Buffer) uint8 {
attrFlags := uint8(0)
attrFlags = setTransitive(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(AtomicAggrAttr)
length := uint8(0)
buf.WriteByte(length)
return 3
}
func (pa *PathAttribute) serializeAggregator(buf *bytes.Buffer) uint8 {
attrFlags := uint8(0)
attrFlags = setOptional(attrFlags)
attrFlags = setTransitive(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(AggregatorAttr)
length := uint8(2)
buf.WriteByte(length)
buf.Write(convert.Uint16Byte(pa.Value.(uint16)))
return 5
}
func (pa *PathAttribute) serializeCommunities(buf *bytes.Buffer) uint8 {
coms := pa.Value.([]uint32)
if len(coms) == 0 {
return 0
}
attrFlags := uint8(0)
attrFlags = setOptional(attrFlags)
attrFlags = setTransitive(attrFlags)
attrFlags = setPartial(attrFlags)
buf.WriteByte(attrFlags)
buf.WriteByte(CommunitiesAttr)
length := uint8(CommunityLen * len(coms))
buf.WriteByte(length)
for _, com := range coms {
buf.Write(convert.Uint32Byte(com))
}
return length
}
func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 {
coms := pa.Value.([]LargeCommunity)
if len(coms) == 0 {
return 0
}
attrFlags := uint8(0)
attrFlags = setOptional(attrFlags)
attrFlags = setTransitive(attrFlags)
attrFlags = setPartial(attrFlags)
buf.WriteByte(attrFlags)
length := uint8(LargeCommunityLen * len(coms))
buf.WriteByte(length)
for _, com := range coms {
buf.Write(convert.Uint32Byte(com.GlobalAdministrator))
buf.Write(convert.Uint32Byte(com.DataPart1))
buf.Write(convert.Uint32Byte(com.DataPart2))
}
return length
}
func LargeCommunityAttributeForString(s string) (*PathAttribute, error) {
strs := strings.Split(s, " ")
coms := make([]LargeCommunity, len(strs))
var err error
for i, str := range strs {
coms[i], err = ParseLargeCommunityString(str)
if err != nil {
return nil, err
}
}
return &PathAttribute{
TypeCode: LargeCommunitiesAttr,
Value: coms,
}, nil
}
func fourBytesToUint32(address [4]byte) uint32 {
return uint32(address[0])<<24 + uint32(address[1])<<16 + uint32(address[2])<<8 + uint32(address[3])
}
func read4BytesAsUint32(buf *bytes.Buffer) (uint32, error) {
b := [4]byte{}
n, err := buf.Read(b[:])
if err != nil {
return 0, err
}
if n != 4 {
return 0, fmt.Errorf("Unable to read as uint32. Expected 4 bytes but got only %d", n)
}
return fourBytesToUint32(b), nil
}