Skip to content
Snippets Groups Projects
path_attributes.go 15.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Oliver Herms's avatar
    Oliver Herms committed
    package packet
    
    import (
    	"bytes"
    	"fmt"
    
    Oliver Herms's avatar
    Oliver Herms committed
    	"github.com/bio-routing/bio-rd/protocols/bgp/types"
    	"github.com/bio-routing/bio-rd/route"
    
    	"github.com/taktv6/tflow2/convert"
    
    Oliver Herms's avatar
    Oliver Herms committed
    )
    
    
    func decodePathAttrs(buf *bytes.Buffer, tpal uint16, opt *Options) (*PathAttribute, error) {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	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)
    
    Oliver Herms's avatar
    Oliver Herms committed
    		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) {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	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 {
    
    Oliver Herms's avatar
    Oliver Herms committed
    			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)
    		}
    
    Oliver Herms's avatar
    Oliver Herms committed
    	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)
    		}
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	case LargeCommunitiesAttr:
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		if err := pa.decodeLargeCommunities(buf); err != nil {
    			return nil, consumed, fmt.Errorf("Failed to decode large communities: %v", err)
    		}
    
    Oliver Herms's avatar
    Oliver Herms committed
    	default:
    
    		if err := pa.decodeUnknown(buf); err != nil {
    			return nil, consumed, fmt.Errorf("Failed to decode unknown attribute: %v", err)
    		}
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    
    	return pa, consumed + pa.Length, nil
    }
    
    
    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
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    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 {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	pa.Value = make(types.ASPath, 0)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	p := uint16(0)
    	for p < pa.Length {
    
    Oliver Herms's avatar
    Oliver Herms committed
    		segment := types.ASPathSegment{}
    		count := uint8(0)
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    		err := decode(buf, []interface{}{&segment.Type, &count})
    
    Oliver Herms's avatar
    Oliver Herms committed
    		if err != nil {
    			return err
    		}
    		p += 2
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    		if segment.Type != types.ASSet && segment.Type != types.ASSequence {
    
    Oliver Herms's avatar
    Oliver Herms committed
    			return fmt.Errorf("Invalid AS Path segment type: %d", segment.Type)
    		}
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    		if count == 0 {
    			return fmt.Errorf("Invalid AS Path segment length: %d", count)
    
    Oliver Herms's avatar
    Oliver Herms committed
    		}
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    		segment.ASNs = make([]uint32, count)
    		for i := uint8(0); i < count; i++ {
    
    			asn, err := pa.decodeASN(buf, asnLength)
    
    Oliver Herms's avatar
    Oliver Herms committed
    			if err != nil {
    				return err
    			}
    
    			p += uint16(asnLength)
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    
    			segment.ASNs[i] = asn
    
    Oliver Herms's avatar
    Oliver Herms committed
    		}
    
    Oliver Herms's avatar
    Oliver Herms committed
    		pa.Value = append(pa.Value.(types.ASPath), segment)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    
    	return nil
    }
    
    
    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
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (pa *PathAttribute) decodeNextHop(buf *bytes.Buffer) error {
    
    	return pa.decodeUint32(buf, "next hop")
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    func (pa *PathAttribute) decodeMED(buf *bytes.Buffer) error {
    
    	return pa.decodeUint32(buf, "MED")
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    func (pa *PathAttribute) decodeLocalPref(buf *bytes.Buffer) error {
    
    	return pa.decodeUint32(buf, "local pref")
    
    Oliver Herms's avatar
    Oliver Herms committed
    }
    
    func (pa *PathAttribute) decodeAggregator(buf *bytes.Buffer) error {
    	aggr := Aggretator{}
    	p := uint16(0)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	err := decode(buf, []interface{}{&aggr.ASN})
    	if err != nil {
    		return err
    	}
    	p += 2
    
    
    	addr := [4]byte{}
    	n, err := buf.Read(addr[:])
    
    Oliver Herms's avatar
    Oliver Herms committed
    	if err != nil {
    		return err
    	}
    	if n != 4 {
    
    		return fmt.Errorf("Unable to read next hop: buf.Read read %d bytes", n)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    
    	aggr.Addr = fourBytesToUint32(addr)
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    	pa.Value = aggr
    
    Oliver Herms's avatar
    Oliver Herms committed
    	return dumpNBytes(buf, pa.Length-p)
    }
    
    
    func (pa *PathAttribute) decodeCommunities(buf *bytes.Buffer) error {
    
    	if pa.Length%CommunityLen != 0 {
    
    		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++ {
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		if err != nil {
    			return err
    		}
    
    		coms[i] = v
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    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
    
    Oliver Herms's avatar
    Oliver Herms committed
    	coms := make([]types.LargeCommunity, count)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    
    	for i := uint16(0); i < count; i++ {
    
    Oliver Herms's avatar
    Oliver Herms committed
    		com := types.LargeCommunity{}
    
    		v, err := read4BytesAsUint32(buf)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		if err != nil {
    			return err
    		}
    		com.GlobalAdministrator = v
    
    
    		v, err = read4BytesAsUint32(buf)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		if err != nil {
    			return err
    		}
    		com.DataPart1 = v
    
    
    		v, err = read4BytesAsUint32(buf)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		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)
    
    		return fmt.Errorf("Unable to decode %s: %v", attrName, err)
    
    	p := uint16(4)
    	err = dumpNBytes(buf, pa.Length-p)
    
    		return fmt.Errorf("dumpNBytes failed: %v", err)
    
    Oliver Herms's avatar
    Oliver Herms committed
    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
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    // Copy create a copy of a path attribute
    
    func (pa *PathAttribute) Copy() *PathAttribute {
    	return &PathAttribute{
    		ExtendedLength: pa.ExtendedLength,
    		Length:         pa.Length,
    		Optional:       pa.Optional,
    		Partial:        pa.Partial,
    		Transitive:     pa.Transitive,
    		TypeCode:       pa.TypeCode,
    		Value:          pa.Value,
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    // 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
    }
    
    Oliver Herms's avatar
    Oliver Herms committed
    // Serialize serializes a path attribute
    func (pa *PathAttribute) Serialize(buf *bytes.Buffer, opt *Options) uint8 {
    
    	pathAttrLen := uint8(0)
    
    	switch pa.TypeCode {
    	case OriginAttr:
    		pathAttrLen = pa.serializeOrigin(buf)
    	case ASPathAttr:
    
    		pathAttrLen = pa.serializeASPath(buf, opt)
    
    	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)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	case LargeCommunitiesAttr:
    
    		pathAttrLen = pa.serializeLargeCommunities(buf)
    
    	default:
    		pathAttrLen = pa.serializeUnknownAttribute(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
    	}
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    	length := uint8(0)
    	segmentsBuf := bytes.NewBuffer(nil)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	for _, segment := range pa.Value.(types.ASPath) {
    
    Oliver Herms's avatar
    Oliver Herms committed
    		segmentsBuf.WriteByte(segment.Type)
    		segmentsBuf.WriteByte(uint8(len(segment.ASNs)))
    
    		for _, asn := range 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
    
    Oliver Herms's avatar
    Oliver Herms committed
    	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)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	addr := pa.Value.(uint32)
    	buf.Write(convert.Uint32Byte(addr))
    
    	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
    }
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    
    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 {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	coms := pa.Value.([]types.LargeCommunity)
    
    	if len(coms) == 0 {
    		return 0
    	}
    
    	attrFlags := uint8(0)
    	attrFlags = setOptional(attrFlags)
    	attrFlags = setTransitive(attrFlags)
    	attrFlags = setPartial(attrFlags)
    	buf.WriteByte(attrFlags)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	buf.WriteByte(LargeCommunitiesAttr)
    
    
    	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 (pa *PathAttribute) serializeUnknownAttribute(buf *bytes.Buffer) uint8 {
    	attrFlags := uint8(0)
    	if pa.Optional {
    		attrFlags = setOptional(attrFlags)
    	}
    	attrFlags = setTransitive(attrFlags)
    
    	buf.WriteByte(attrFlags)
    	buf.WriteByte(pa.TypeCode)
    
    	b := pa.Value.([]byte)
    	buf.WriteByte(uint8(len(b)))
    	buf.Write(b)
    
    	return uint8(len(b) + 2)
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    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) {
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	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)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	}
    
    	return fourBytesToUint32(b), nil
    }
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    // AddOptionalPathAttributes adds optional path attributes to linked list pa
    func (pa *PathAttribute) AddOptionalPathAttributes(p *route.Path) *PathAttribute {
    	current := pa
    
    	if len(p.BGPPath.Communities) > 0 {
    		communities := &PathAttribute{
    			TypeCode: CommunitiesAttr,
    			Value:    p.BGPPath.Communities,
    		}
    		current.Next = communities
    		current = communities
    	}
    
    	if len(p.BGPPath.LargeCommunities) > 0 {
    		largeCommunities := &PathAttribute{
    			TypeCode: LargeCommunitiesAttr,
    			Value:    p.BGPPath.LargeCommunities,
    		}
    		current.Next = largeCommunities
    		current = largeCommunities
    	}
    
    	return current
    }
    
    // PathAttributes converts a path object into a linked list of path attributes
    func PathAttributes(p *route.Path) (*PathAttribute, error) {
    	asPath := &PathAttribute{
    		TypeCode: ASPathAttr,
    		Value:    p.BGPPath.ASPath,
    	}
    
    	origin := &PathAttribute{
    		TypeCode: OriginAttr,
    		Value:    p.BGPPath.Origin,
    	}
    	asPath.Next = origin
    
    	nextHop := &PathAttribute{
    		TypeCode: NextHopAttr,
    		Value:    p.BGPPath.NextHop,
    	}
    	origin.Next = nextHop
    
    	localPref := &PathAttribute{
    		TypeCode: LocalPrefAttr,
    		Value:    p.BGPPath.LocalPref,
    	}
    	nextHop.Next = localPref
    
    	optionals := localPref.AddOptionalPathAttributes(p)
    
    	last := optionals
    	for _, unknownAttr := range p.BGPPath.UnknownAttributes {
    		last.Next = &PathAttribute{
    			TypeCode:   unknownAttr.TypeCode,
    			Optional:   unknownAttr.Optional,
    			Transitive: unknownAttr.Transitive,
    			Partial:    unknownAttr.Partial,
    			Value:      unknownAttr.Value,
    		}
    		last = last.Next
    	}
    
    	return asPath, nil
    }