Skip to content
Snippets Groups Projects
mp_reach_nlri.go 2.58 KiB
Newer Older
  • Learn to ignore specific revisions
  • Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    package packet
    
    import (
    	"bytes"
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    
    	"github.com/taktv6/tflow2/convert"
    
    	bnet "github.com/bio-routing/bio-rd/net"
    
    	"github.com/bio-routing/bio-rd/util/decode"
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    // MultiProtocolReachNLRI represents network layer reachability information for one prefix of an IP address family (rfc4760)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    type MultiProtocolReachNLRI struct {
    
    	AFI      uint16
    	SAFI     uint8
    	NextHop  bnet.IP
    	Prefixes []bnet.Prefix
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	PathID   uint32
    
    func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer, opt *EncodeOptions) uint16 {
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	nextHop := n.NextHop.Bytes()
    
    	tempBuf := bytes.NewBuffer(nil)
    	tempBuf.Write(convert.Uint16Byte(n.AFI))
    	tempBuf.WriteByte(n.SAFI)
    
    	tempBuf.WriteByte(uint8(len(nextHop)))
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	tempBuf.Write(nextHop)
    	tempBuf.WriteByte(0) // RESERVED
    
    	for _, pfx := range n.Prefixes {
    
    		if opt.UseAddPath {
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    			tempBuf.Write(convert.Uint32Byte(n.PathID))
    		}
    
    		tempBuf.Write(serializePrefix(pfx))
    	}
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    
    	buf.Write(tempBuf.Bytes())
    
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	return uint16(tempBuf.Len())
    
    
    func deserializeMultiProtocolReachNLRI(b []byte) (MultiProtocolReachNLRI, error) {
    	n := MultiProtocolReachNLRI{}
    	nextHopLength := uint8(0)
    
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	variableLength := len(b) - 4 // 4 <- AFI + SAFI + NextHopLength
    	if variableLength <= 0 {
    		return n, fmt.Errorf("Invalid length of MP_REACH_NLRI: expected more than 4 bytes but got %d", len(b))
    	}
    
    	variable := make([]byte, variableLength)
    
    	fields := []interface{}{
    		&n.AFI,
    		&n.SAFI,
    		&nextHopLength,
    		&variable,
    	}
    
    	err := decode.Decode(bytes.NewBuffer(b), fields)
    
    	if err != nil {
    		return MultiProtocolReachNLRI{}, err
    	}
    
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	budget := variableLength
    	if budget < int(nextHopLength) {
    		return MultiProtocolReachNLRI{},
    			fmt.Errorf("Failed to decode next hop IP: expected %d bytes for NLRI, only %d remaining", nextHopLength, budget)
    	}
    
    
    	n.NextHop, err = bnet.IPFromBytes(variable[:nextHopLength])
    	if err != nil {
    		return MultiProtocolReachNLRI{}, fmt.Errorf("Failed to decode next hop IP: %v", err)
    	}
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	budget -= int(nextHopLength)
    
    
    	n.Prefixes = make([]bnet.Prefix, 0)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	if budget == 0 {
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		return n, nil
    	}
    
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	variable = variable[1+nextHopLength:] // 1 <- RESERVED field
    
    
    	idx := uint16(0)
    	for idx < uint16(len(variable)) {
    		pfxLen := variable[idx]
    
    		numBytes := uint16(BytesInAddr(pfxLen))
    
    		idx++
    
    		r := uint16(len(variable)) - idx
    		if r < numBytes {
    			return MultiProtocolReachNLRI{}, fmt.Errorf("expected %d bytes for NLRI, only %d remaining", numBytes, r)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		}
    
    		start := idx
    		end := idx + numBytes
    		pfx, err := deserializePrefix(variable[start:end], pfxLen, n.AFI)
    
    		if err != nil {
    			return MultiProtocolReachNLRI{}, err
    		}
    		n.Prefixes = append(n.Prefixes, pfx)
    
    
    		idx = idx + numBytes