Skip to content
Snippets Groups Projects
update_sender.go 8.26 KiB
Newer Older
  • Learn to ignore specific revisions
  • Oliver Herms's avatar
    Oliver Herms committed
    package server
    
    import (
    
    	"errors"
    	"io"
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
    
    Oliver Herms's avatar
    Oliver Herms committed
    	"github.com/bio-routing/bio-rd/route"
    	"github.com/bio-routing/bio-rd/routingtable"
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    	bnet "github.com/bio-routing/bio-rd/net"
    	log "github.com/sirupsen/logrus"
    
    Oliver Herms's avatar
    Oliver Herms committed
    // UpdateSender converts table changes into BGP update messages
    
    Oliver Herms's avatar
    Oliver Herms committed
    type UpdateSender struct {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	routingtable.ClientManager
    
    	afi       uint16
    	safi      uint8
    
    	toSendMu  sync.Mutex
    	toSend    map[string]*pathPfxs
    	destroyCh chan struct{}
    }
    
    type pathPfxs struct {
    	path *route.Path
    	pfxs []bnet.Prefix
    }
    
    
    func newUpdateSender(fsm *FSM, afi uint16, safi uint8) *UpdateSender {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	return &UpdateSender{
    
    		afi:       afi,
    		safi:      safi,
    
    		iBGP:      fsm.peer.localASN == fsm.peer.peerASN,
    
    		rrClient:  fsm.peer.routeReflectorClient,
    
    		toSend:    make(map[string]*pathPfxs),
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (u *UpdateSender) Start(aggrTime time.Duration) {
    	go u.sender(aggrTime)
    
    }
    
    // Destroy destroys everything (with greetings to Hatebreed)
    func (u *UpdateSender) Destroy() {
    	u.destroyCh <- struct{}{}
    }
    
    // AddPath adds path p for pfx to toSend queue
    func (u *UpdateSender) AddPath(pfx bnet.Prefix, p *route.Path) error {
    	u.toSendMu.Lock()
    
    	hash := p.BGPPath.ComputeHash()
    	if _, exists := u.toSend[hash]; exists {
    		u.toSend[hash].pfxs = append(u.toSend[hash].pfxs, pfx)
    		u.toSendMu.Unlock()
    
    		return nil
    
    	u.toSend[p.BGPPath.ComputeHash()] = &pathPfxs{
    		path: p,
    		pfxs: []bnet.Prefix{
    			pfx,
    
    	u.toSendMu.Unlock()
    	return nil
    }
    
    // sender serializes BGP update messages
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (u *UpdateSender) sender(aggrTime time.Duration) {
    	ticker := time.NewTicker(aggrTime)
    
    	var err error
    	var pathAttrs *packet.PathAttribute
    	var budget int
    
    	for {
    		select {
    		case <-u.destroyCh:
    			return
    		case <-ticker.C:
    		}
    
    		u.toSendMu.Lock()
    
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		overhead := u.updateOverhead()
    
    
    		for key, pathNLRIs := range u.toSend {
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    			budget = packet.MaxLen - packet.HeaderLen - packet.MinUpdateLen - int(pathNLRIs.path.BGPPath.Length()) - overhead
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    
    
    			pathAttrs, err = packet.PathAttributes(pathNLRIs.path, u.iBGP, u.rrClient)
    
    			if err != nil {
    				log.Errorf("Unable to get path attributes: %v", err)
    				continue
    			}
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    			updatesPrefixes := make([][]bnet.Prefix, 0, 1)
    			prefixes := make([]bnet.Prefix, 0, 1)
    
    Oliver Herms's avatar
    Oliver Herms committed
    				budget -= int(packet.BytesInAddr(pfx.Pfxlen())) + 1
    
    				if u.fsm.options.AddPathRX {
    					budget -= 4
    				}
    
    
    				if budget < 0 {
    					updatesPrefixes = append(updatesPrefixes, prefixes)
    
    Oliver Herms's avatar
    Oliver Herms committed
    					prefixes = make([]bnet.Prefix, 0, 1)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    					budget = packet.MaxLen - int(pathNLRIs.path.BGPPath.Length()) - overhead
    
    Oliver Herms's avatar
    Oliver Herms committed
    			if len(prefixes) > 0 {
    				updatesPrefixes = append(updatesPrefixes, prefixes)
    			}
    
    
    			delete(u.toSend, key)
    			u.toSendMu.Unlock()
    
    			u.sendUpdates(pathAttrs, updatesPrefixes, pathNLRIs.path.BGPPath.PathIdentifier)
    			u.toSendMu.Lock()
    		}
    
    Oliver Herms's avatar
    Oliver Herms committed
    		u.toSendMu.Unlock()
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    func (u *UpdateSender) updateOverhead() int {
    
    	if !u.fsm.options.SupportsMultiProtocol {
    		return 0
    
    	addrLen := packet.IPv4AFI
    	if u.afi == packet.IPv6AFI {
    		addrLen = packet.IPv6Len
    	}
    
    	// since we are replacing the next hop attribute IPv4Len has to be subtracted, we also add another byte for extended length
    	return packet.AFILen + packet.SAFILen + 1 + addrLen - packet.IPv4Len + 1
    
    func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefixes [][]bnet.Prefix, pathID uint32) {
    	var err error
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	for _, prefixes := range updatePrefixes {
    		update := u.updateMessageForPrefixes(prefixes, pathAttrs, pathID)
    
    		if update == nil {
    			log.Errorf("Failed to create update: Neighbor does not support multi protocol.")
    			return
    		}
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options)
    		if err != nil {
    			log.Errorf("Failed to serialize and send: %v", err)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    func (u *UpdateSender) updateMessageForPrefixes(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
    
    	if u.afi == packet.IPv4AFI && u.safi == packet.UnicastSAFI {
    		return u.bgpUpdate(pfxs, pa, pathID)
    	}
    
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	if u.fsm.options.SupportsMultiProtocol {
    		return u.bgpUpdateMultiProtocol(pfxs, pa, pathID)
    	}
    
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    }
    
    func (u *UpdateSender) bgpUpdate(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
    	update := &packet.BGPUpdate{
    		PathAttributes: pa,
    	}
    
    	var nlri *packet.NLRI
    	for _, pfx := range pfxs {
    		nlri = &packet.NLRI{
    			PathIdentifier: pathID,
    			IP:             pfx.Addr().ToUint32(),
    			Pfxlen:         pfx.Pfxlen(),
    			Next:           update.NLRI,
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		update.NLRI = nlri
    	}
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	return update
    }
    
    func (u *UpdateSender) bgpUpdateMultiProtocol(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
    	pa, nextHop := u.copyAttributesWithoutNextHop(pa)
    
    	attrs := &packet.PathAttribute{
    		TypeCode: packet.MultiProtocolReachNLRICode,
    		Value: packet.MultiProtocolReachNLRI{
    
    			AFI:      u.afi,
    			SAFI:     u.safi,
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    			NextHop:  nextHop,
    			Prefixes: pfxs,
    
    			PathID:   pathID,
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		},
    	}
    	attrs.Next = pa
    
    	return &packet.BGPUpdate{
    		PathAttributes: attrs,
    	}
    }
    
    func (u *UpdateSender) copyAttributesWithoutNextHop(pa *packet.PathAttribute) (attrs *packet.PathAttribute, nextHop bnet.IP) {
    
    	var curCopy, lastCopy *packet.PathAttribute
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    	for cur := pa; cur != nil; cur = cur.Next {
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		if cur.TypeCode == packet.NextHopAttr {
    			nextHop = cur.Value.(bnet.IP)
    		} else {
    
    			curCopy = cur.Copy()
    
    			if lastCopy == nil {
    				attrs = curCopy
    			} else {
    				lastCopy.Next = curCopy
    			}
    			lastCopy = curCopy
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    
    	return attrs, nextHop
    
    Oliver Herms's avatar
    Oliver Herms committed
    // RemovePath withdraws prefix `pfx` from a peer
    
    func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool {
    
    	err := u.withdrawPrefix(pfx, p)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	if err != nil {
    		log.Errorf("Unable to withdraw prefix: %v", err)
    		return false
    	}
    	return true
    
    func (u *UpdateSender) withdrawPrefix(pfx bnet.Prefix, p *route.Path) error {
    	if u.fsm.options.SupportsMultiProtocol {
    
    		return u.withDrawPrefixesMultiProtocol(u.fsm.con, pfx, p)
    	}
    
    	return u.withDrawPrefixesAddPath(u.fsm.con, pfx, p)
    }
    
    // withDrawPrefixes generates a BGPUpdate message and write it to the given
    // io.Writer.
    func (u *UpdateSender) withDrawPrefixes(out io.Writer, prefixes ...bnet.Prefix) error {
    	if len(prefixes) < 1 {
    		return nil
    	}
    
    	var rootNLRI *packet.NLRI
    	var currentNLRI *packet.NLRI
    	for _, pfx := range prefixes {
    		if rootNLRI == nil {
    			rootNLRI = &packet.NLRI{
    				IP:     pfx.Addr().ToUint32(),
    				Pfxlen: pfx.Pfxlen(),
    			}
    			currentNLRI = rootNLRI
    		} else {
    			currentNLRI.Next = &packet.NLRI{
    				IP:     pfx.Addr().ToUint32(),
    				Pfxlen: pfx.Pfxlen(),
    			}
    			currentNLRI = currentNLRI.Next
    		}
    	}
    
    	update := &packet.BGPUpdate{
    		WithdrawnRoutes: rootNLRI,
    	}
    
    	return serializeAndSendUpdate(out, update, u.fsm.options)
    
    }
    
    // withDrawPrefixesAddPath generates a BGPUpdateAddPath message and write it to the given
    // io.Writer.
    func (u *UpdateSender) withDrawPrefixesAddPath(out io.Writer, pfx bnet.Prefix, p *route.Path) error {
    	if p.Type != route.BGPPathType {
    		return errors.New("wrong path type, expected BGPPathType")
    	}
    
    	if p.BGPPath == nil {
    		return errors.New("got nil BGPPath")
    	}
    
    	update := &packet.BGPUpdate{
    		WithdrawnRoutes: &packet.NLRI{
    			PathIdentifier: p.BGPPath.PathIdentifier,
    			IP:             pfx.Addr().ToUint32(),
    			Pfxlen:         pfx.Pfxlen(),
    		},
    	}
    
    	return serializeAndSendUpdate(out, update, u.fsm.options)
    }
    
    func (u *UpdateSender) withDrawPrefixesMultiProtocol(out io.Writer, pfx bnet.Prefix, p *route.Path) error {
    	pathID := uint32(0)
    	if p.BGPPath != nil {
    		pathID = p.BGPPath.PathIdentifier
    	}
    
    	update := &packet.BGPUpdate{
    		PathAttributes: &packet.PathAttribute{
    			TypeCode: packet.MultiProtocolUnreachNLRICode,
    			Value: packet.MultiProtocolUnreachNLRI{
    				AFI:      u.afi,
    				SAFI:     u.safi,
    				Prefixes: []bnet.Prefix{pfx},
    				PathID:   pathID,
    			},
    		},
    
    	return serializeAndSendUpdate(out, update, u.fsm.options)
    
    Oliver Herms's avatar
    Oliver Herms committed
    // UpdateNewClient does nothing
    
    Oliver Herms's avatar
    Oliver Herms committed
    func (u *UpdateSender) UpdateNewClient(client routingtable.RouteTableClient) error {
    
    	log.Warningf("BGP Update Sender: UpdateNewClient not implemented")
    
    Oliver Herms's avatar
    Oliver Herms committed
    	return nil
    
    // RouteCount returns the number of stored routes
    
    func (u *UpdateSender) RouteCount() int64 {
    
    	log.Warningf("BGP Update Sender: RouteCount not implemented")