Skip to content
Snippets Groups Projects
fib.go 6.76 KiB
Newer Older
  • Learn to ignore specific revisions
  • package fib
    
    import (
    
    	bnet "github.com/bio-routing/bio-rd/net"
    
    	"github.com/bio-routing/bio-rd/route"
    
    	"github.com/bio-routing/bio-rd/routingtable"
    
    	"github.com/bio-routing/bio-rd/routingtable/vrf"
    	"github.com/pkg/errors"
    
    	log "github.com/sirupsen/logrus"
    
    type fibOsAdapter interface {
    
    	addPath(pfx bnet.Prefix, paths []*route.FIBPath) error
    
    cedi's avatar
    cedi committed
    	removePath(pfx bnet.Prefix, path *route.FIBPath) error
    
    cedi's avatar
    cedi committed
    	getFibName() string
    	needsVrfID() bool
    	setVrfID(vrdID string) error
    
    // FIB is forwarding information base
    type FIB struct {
    
    	vrf           *vrf.VRF
    	osAdapter     fibOsAdapter
    
    cedi's avatar
    cedi committed
    	pathTable     map[bnet.Prefix][]*route.FIBPath
    
    	pathsMu       sync.RWMutex
    	clientManager *routingtable.ClientManager
    
    // New creates a new Netlink object and returns the pointer to it
    
    cedi's avatar
    cedi committed
    func New(v *vrf.VRF) (*FIB, error) {
    	if v == nil {
    		return nil, fmt.Errorf("Cannot create FIB: No VRF given. Please use at least default VRF")
    	}
    
    
    cedi's avatar
    cedi committed
    		vrf:       v,
    		pathTable: make(map[bnet.Prefix][]*route.FIBPath),
    
    cedi's avatar
    cedi committed
    	n.clientManager = routingtable.NewClientManager(n)
    
    cedi's avatar
    cedi committed
    	return n, nil
    
    cedi's avatar
    cedi committed
    // SetVrfID specifies the vrf id for the fib implementation.
    // Some fib implementations don't need a VRF-ID.
    // This method will return an error, in case the hardware specific fib implementation
    // doesn't support or need a vrfId
    func (f *FIB) SetVrfID(vrfID string) error {
    	if f.osAdapter != nil {
    		return fmt.Errorf("osAdapter is nil")
    	}
    
    	if !f.osAdapter.needsVrfID() {
    		return fmt.Errorf("The fib implementation for %s doesn't need a routing table identifier", f.osAdapter.getFibName())
    	}
    
    	return f.osAdapter.setVrfID(vrfID)
    }
    
    
    // Start the Netlink module
    
    func (f *FIB) Start() error {
    
    cedi's avatar
    cedi committed
    	if f.osAdapter == nil {
    		return fmt.Errorf("osAdapter is not loaded correctly")
    	}
    
    
    	// connect all RIBs
    
    	options := routingtable.ClientOptions{
    
    		BestOnly: false,
    		EcmpOnly: false,
    		MaxPaths: ^uint(0), // max int
    
    	// register to all ribs in VRF
    
    cedi's avatar
    cedi committed
    	rib4, found := f.vrf.RIBByName("inet.0")
    	if found {
    		// from locRib to FIB
    		rib4.RegisterWithOptions(f, options)
    	}
    
    cedi's avatar
    cedi committed
    	rib6, found := f.vrf.RIBByName("inet6.0")
    	if found {
    
    cedi's avatar
    cedi committed
    		rib6.RegisterWithOptions(f, options)
    
    	return nil
    }
    
    // RouteCount returns the current count of paths in the FIB
    func (f *FIB) RouteCount() int64 {
    	f.pathsMu.RLock()
    	defer f.pathsMu.RUnlock()
    
    
    	for _, paths := range f.pathTable {
    		fibCount += int64(len(paths))
    	}
    
    cedi's avatar
    cedi committed
    
    
    	return fibCount
    }
    
    // AddPath is called from the RIB when an path is added there
    func (f *FIB) AddPath(pfx bnet.Prefix, path *route.Path) error {
    	// Convert Path to FIBPath
    	addPath, err := route.NewFIBPathFromPath(path)
    	if err != nil {
    		return errors.Wrap(err, "Could not convert path to FIB path")
    
    	}
    
    	f.pathsMu.Lock()
    	defer f.pathsMu.Unlock()
    
    
    	paths, found := f.pathTable[pfx]
    	newPath := false
    	if found {
    		if !addPath.ContainedIn(paths) {
    			paths = append(paths, addPath)
    			newPath = true
    
    	} else {
    		paths = []*route.FIBPath{addPath}
    		newPath = true
    
    	if newPath {
    		err = f.osAdapter.addPath(pfx, paths)
    
    cedi's avatar
    cedi committed
    			log.Errorf("Can't add Path to underlying OS Layer: %v", err)
    
    			return errors.Wrap(err, "Can't add Path to underlying OS Layer")
    
    		// Save new paths
    		f.pathTable[pfx] = paths
    
    }
    
    // RemovePath adds the element from the FIB List
    
    // returns true if something was removed, false otherwise
    
    func (f *FIB) RemovePath(pfx bnet.Prefix, path *route.Path) bool {
    
    	// Convert Path to FIBPath
    	delPath, err := route.NewFIBPathFromPath(path)
    	if err != nil {
    		log.Errorf("Could not convert path to FIB path: %v", err)
    		return false
    
    	}
    
    	f.pathsMu.Lock()
    	defer f.pathsMu.Unlock()
    
    	existingPaths, found := f.pathTable[pfx]
    	if !found {
    		return false
    	}
    
    
    	pathCountBeforeDel := len(existingPaths)
    
    
    	for idx, p := range existingPaths {
    
    cedi's avatar
    cedi committed
    		if !p.Equals(delPath) {
    
    			continue
    		}
    
    		err := f.osAdapter.removePath(pfx, delPath)
    		if err != nil {
    			log.Errorf("Can't remove Path from underlying OS Layer: %v", err)
    
    			// TODO: continue here and let the path in the paths table, or should we just log the error and remove the path regardles if the underlaying operation fails
    
    		// remove path from existing paths
    		existingPaths = append(existingPaths[:idx], existingPaths[idx+1:]...)
    
    	f.pathTable[pfx] = existingPaths
    
    
    	return pathCountBeforeDel > len(existingPaths)
    
    }
    
    // If inFibButNotIncmpTo=true the diff will show which parts of cmpTo are inside fib,
    // if inFibButNotIncmpTo=false the diff will show which parts of cmpTo are not inside the fib
    // this function does not aquire a mutex lock! Be careful!
    
    cedi's avatar
    cedi committed
    func (f *FIB) compareFibPfxPath(cmpTo []*route.PrefixPathsPair, inFibButNotIncmpTo bool) []*route.PrefixPathsPair {
    
    cedi's avatar
    cedi committed
    	pfxPathsDiff := make([]*route.PrefixPathsPair, 0)
    
    	if len(cmpTo) == 0 {
    		if inFibButNotIncmpTo {
    			for key, value := range f.pathTable {
    				pfxPathsDiff = append(pfxPathsDiff, &route.PrefixPathsPair{
    					Pfx:   key,
    					Paths: value,
    				})
    			}
    		}
    
    		return pfxPathsDiff
    	}
    
    
    	f.pathsMu.Lock()
    	defer f.pathsMu.Unlock()
    
    
    cedi's avatar
    cedi committed
    	if len(f.pathTable) == 0 {
    		if inFibButNotIncmpTo {
    			return pfxPathsDiff
    		}
    
    		return cmpTo
    	}
    
    	for _, pfxPathPair := range cmpTo {
    		paths, found := f.pathTable[pfxPathPair.Pfx]
    
    
    		if found {
    			if inFibButNotIncmpTo {
    
    cedi's avatar
    cedi committed
    				pfxPathPair.Paths = route.FIBPathsDiff(paths, pfxPathPair.Paths)
    
    cedi's avatar
    cedi committed
    				pfxPathPair.Paths = route.FIBPathsDiff(pfxPathPair.Paths, paths)
    
    cedi's avatar
    cedi committed
    
    
    cedi's avatar
    cedi committed
    			pfxPathsDiff = append(pfxPathsDiff, pfxPathPair)
    
    cedi's avatar
    cedi committed
    		}
    
    
    // Stop stops the device server
    func (f *FIB) Stop() {
    
    }
    
    // UpdateNewClient Not supported for NetlinkWriter, since the writer is not observable
    func (f *FIB) UpdateNewClient(routingtable.RouteTableClient) error {
    
    cedi's avatar
    cedi committed
    	f.pathsMu.RLock()
    	f.pathsMu.RUnlock()
    
    	for _, client := range f.clientManager.Clients() {
    		for pfx, addPaths := range f.pathTable {
    			for _, addP := range addPaths {
    				client.AddPath(pfx, &route.Path{
    					Type:    route.FIBPathType,
    
    cedi's avatar
    cedi committed
    					FIBPath: addP,
    
    cedi's avatar
    cedi committed
    				})
    			}
    		}
    	}
    
    	return nil
    
    }
    
    // Register Not supported for NetlinkWriter, since the writer is not observable
    
    cedi's avatar
    cedi committed
    func (f *FIB) Register(client routingtable.RouteTableClient) {
    	f.clientManager.RegisterWithOptions(client, routingtable.ClientOptions{BestOnly: true})
    
    }
    
    // RegisterWithOptions Not supported, since the writer is not observable
    
    cedi's avatar
    cedi committed
    func (f *FIB) RegisterWithOptions(client routingtable.RouteTableClient, opt routingtable.ClientOptions) {
    	f.clientManager.RegisterWithOptions(client, opt)
    
    }
    
    // Unregister is not supported, since the writer is not observable
    
    cedi's avatar
    cedi committed
    func (f *FIB) Unregister(client routingtable.RouteTableClient) {
    	f.clientManager.Unregister(client)
    
    cedi's avatar
    cedi committed
    // ClientCount returns how many clients are connected
    
    func (f *FIB) ClientCount() uint64 {
    
    cedi's avatar
    cedi committed
    	return f.clientManager.ClientCount()
    
    }
    
    // Dump is currently not supported
    func (f *FIB) Dump() []*route.Route {
    	return nil